$ clang --target=aarch64-linux-gnu -O2 -S /tmp/first.ll -o - -Wno-override-module | sed -n '/%vector.body/,/b.ne/p'
what it printed on the machine that wrote this lab
.LBB0_8: // %vector.body
// =>This Inner Loop Header: Depth=1
ldp q1, q2, [x10, #-16]
subs x11, x11, #8
ldp q3, q4, [x9, #-16]
add x10, x10, #32
fmul v1.4s, v1.4s, v0.s[0]
fmul v2.4s, v2.4s, v0.s[0]
fadd v1.4s, v1.4s, v3.4s
fadd v2.4s, v2.4s, v4.4s
stp q1, q2, [x9, #-16]
add x9, x9, #32
b.ne .LBB0_8
$ clang --target=aarch64-linux-gnu -O2 -S /tmp/first.ll -o - -Wno-override-module | grep -c 'vector.memcheck'
what it printed on the machine that wrote this lab
1
Three things to learn from twelve instructions:
(a) The IR was target-independent; the backend picked AArch64 at the
last moment. Whatever MLIR doesn't decide, LLVM decides.
(b) MLIR emitted a SCALAR loop, and LLVM's loop vectorizer rescued it
(NEON v1.4s, 8 floats/iteration). But it had to add a runtime
`vector.memcheck`: x and y are two memrefs that MIGHT alias, so it
checks at runtime and keeps a scalar fallback. MLIR code that is
vectorized BEFORE lowering (lab 04_optimization) needs no such check,
because linalg semantics already say what may be read and written.
(c) `fmul` + `fadd`, not one `fmla`. C code would get a fused multiply-add
here, but MLIR's arith ops default to fastmath<none> (lab 01 STEP 2),
and LLVM may not CONTRACT a*b+c into fma without permission. Two
instructions instead of one, in the hottest loop. Lab 04_optimization/03
turns the flag on and measures it. This is a real
"fast with X / slow without X" case you'll see in a production MLIR backend.