Thanks @pag and @rengolin’s comments. Using @pag 's example:
%buff0_lifeline = memref.lifetime_start %buff0 : memref<32xf32> // [ lifetime 0
... // uses are all `%buff0_lifeline`, not `%buff0`.
memref.lifetime_end %buff0_lifeline : memref<32xf32> // lifetime 0 ]
The lifetime of a memref value
(i.e. %buff0_lifeline) is indeed de-coupled from the buffer
(%buff0) that holds it. Initially, a lifeline may be associated with a unique buffer (e.g. %buff0_life is, at first, associated with %buff0). After applying buffer reuse optimization (like register allocation), multiple lifelines may be associated with the same buffer. (since for accelerator, stack or on-chip memory is limited, having buffer reuse is critically important for our use case)
@pag 's proposal is indeed similar in spirit to @mehdi_amini 's earlier example where %t1 (the value) is associated with %buff. In fact, multiple values (%t1, %t2, %t3) can be associated with the same %buff.
func.func @example(%A : memref<32xf32>, %OUT : memref<32xf32>) {
%buff = memref.alloca() : memref<64xf32>
%t1 = memref.view %buff[0...32] : memref<32xf32>
%t2 = memref.view %buff[32...64] : memref<32xf32> // disjoint from %t1
%t3 = memref.view %buff[0...64] : memref<64xf32> // overlap with %t1 and %t2