[RFC] intrinsics for load/store-with-length semantics

We propose introducing two new intrinsics: llvm.variable.length.load and llvm.variable.length.store.
We have implemented the infrastructure for defining and lowering these in this phabricator patch: https://reviews.llvm.org/D86693

These represent the semantics of loading and storing a variable number of bytes to a fixed-width register;
in effect, a masked load or store where the only active lanes are given by a contiguous block.

There are a few reasons for separately representing this kind of operation, even though as noted it can be represented by a subset of masked loads and stores.

  • For targets that have separate hardware support for this kind of operation, it makes it easier to generate an optimal lowering. We are currently working on enabling this for some PowerPC subtargets. In particular, there are some targets that support this kind of operation but not masked loads and stores in general, including Power9.

  • Scalarization of this pattern can be done using a number of branches logarithmic in the width of the register, rather than the linear case for general masked operations.

  • Scalarized residuals of vectorized loops tend to employ these semantics (tail-folding in particular), so this infrastructure can be used to make more specific optimization decisions for lowering loop residuals. This also pulls out the logic of how to represent and lower such semantics from the loop vectorizer, allowing for better separation of concerns. Our group is currently working on implementing some of these optimizations in the loop vectorizer.

  • Representing these semantics using current masked intrinsics would require introducing intermediate steps to generate the appropriate bitmasks, and then detecting them during lowering. This introduces nontrivial complexity that we want to avoid. If it isn’t possible to detect all cases during lowering by inspecting the AST, expensive runtime checks would then have to be introduced.

Please refer to the phabricator patch for our implementation, which includes intrinsic definitions, new SDAG nodes, and support for type widening and scalarization.

“The vectorizer needs this” seems like a fair reason to add it to the IR.

Pattern-matching an llvm.masked.load with an llvm.get.active.lane.mask operand might not be that terrible? If that works, I’d prefer to go with that because we already have that codepath. Otherwise, adding a new intrinsic seems okay.

There’s a possibility that we’ll want a version of llvm.masked.load that takes both a length and a mask, eventually. See https://reviews.llvm.org/D57504 . Not completely sure how that should interact with this proposal.

-Eli

Hi Hussain,

Thanks for your patch! We are working on a similar extension (Vector Predication) with the goal of enabling variable length on all SIMD operations. We have a reference patch that includes variable-length load store intrinsics. You find it here: https://reviews.llvm.org/D57504
Integer VP intrinsics are upstream and documented here: https://llvm.org/docs/LangRef.html#vector-predication-intrinsics

The VP load/store intrinsics look like this:

     @llvm.vp.load(%ptr, %mask, %vector_length)
    @llvm.vp.store(%data, %ptr, %mask, %vector_length)

where the %vector_length argument specifies the variable length of the operation. There is also a %mask but you can simply pass the constant-true mask to disable it.
Now i wonder, would VP load/store cover your use case? It'd be great if your patch, the scalarization logic, TTI, could be integrated into the framework of VP intrinsics.

- Simon