Phase 1, week 7retrospective

RVV disassembly check

Disassembly is right when every inner-loop instruction is explainable as load, MAC, vsetvli, or control, and no spills appear.


The listing, filtered

riscv64-linux-gnu-objdump -d bin/matmul_rvv | sed -n '/<matmul>:/,$p'
riscv64-linux-gnu-objdump -d bin/matmul_rvv | grep -c vfmacc

The inner loop has a shape. Yours should look like this, with the branch target named rather than printed as an address:

.Lloop:
vsetvli     a3,a0,e32,m2,ta,ma
vle32.v     v10,(a2)
vle32.v     v12,(a1)
vfmacc.vv   v8,v10,v12
sub         a0,a0,a3
slli        a4,a3,2
add         a1,a1,a4
add         a2,a2,a4
bne         a0,zero,.Lloop

Nine instructions, and every one of them is a load, a MAC, or loop control. vsetvli is the control that also produces the vl in a3. The two vle32.v fetch the next slice of A and of B. vfmacc.vv adds v10 * v12 into v8, which is the accumulator and lives across iterations, so no load in the loop body is allowed to target it. The sub produces the next AVL, slli turns lanes into bytes, the two adds bump pointers, and bne closes the loop. If you cannot assign a line to one of those three jobs, you do not yet know what the loop does.

Contraction is a permission

vfmacc is fused. It appears only when the multiply and the add may become one operation: in MLIR the contract fast-math flag has to reach the arith ops, and in C it depends on -ffp-contract, where fast permits fusion and off forbids it. With contraction disabled the loop shows vfmul.vv followed by vfadd.vv: correct, one instruction longer, not a bug.

The test is not whether vfmacc occurs but whether each instruction is explainable: a vfmul and vfadd you can explain beat a vfmacc you cannot.

The mnemonics are fixed by the ISA. What moves between LLVM releases is when the backend fuses a multiply and add, and which policy pair it prints, so confirm those against the release you pinned rather than an older listing you saved.

Things that break

vs1r.v and vl1r.v in the loop body are whole-register spills and reloads, so the live tile does not fit the register budget; the fix is a smaller tile or a smaller LMUL, not another optimisation flag. A vsetvli zero,... that nothing consumes means the length was discarded, so the pointer advance must be VLMAX and any other stride walks off the end of the buffer. A slli by 2 in an f64 kernel is a half-finished type change: the listing is identical in shape and the stride is wrong.

Sources

A version records the documentation the claim was checked against, not a house pin. Confirm flags with --help on the release you installed.

Check yourself

The loop shows `vfmul.vv` then `vfadd.vv` where you expected `vfmacc.vv`. What is the likely cause?

`vs1r.v` and `vl1r.v` appear inside the inner loop. What does that tell you?

A `vsetvli zero,a0,e32,m2,ta,ma` in the loop, with nothing consuming the `vl`. What must be true of the pointer advance?

0 / 3 correct

← back to the map