[Mlir-commits] [mlir] [mlir][VectorToLLVM] emit `inbounds|nuw` GEP flags when lowering `vector.load/store` (PR #202118)
Federico Bruzzone
llvmlistbot at llvm.org
Sun Jun 14 04:29:35 PDT 2026
FedericoBruzzone wrote:
> 1. I could've sworn we didn't allow negative strides in memref, so `nuw` and `inbounds` on `memref.load` is correct
Unfortunately, I think that this is not true, but please correct me if I'm wrong 🙏
My conclusions are guided by the following two tests:
https://github.com/llvm/llvm-project/blob/db210c5e33643a8154329a607b5b6dcd419271df/mlir/test/Dialect/MemRef/canonicalize.mlir#L1290-L1305
https://github.com/llvm/llvm-project/blob/db210c5e33643a8154329a607b5b6dcd419271df/mlir/test/Conversion/MemRefToLLVM/expand-then-convert-to-llvm.mlir#L356-L376
If we confirm, I'd be glad to send a subsequent patch for this (with the docs fixed).
> 2. I still think that putting flags on `vector.load` for "this has 0 <= idx < dim for all dimensions" and "none of these indices are negative" as extra assumptions you can encode is the right move - it means that if you don't set these flags, we just don't say `inbounds` and `nuw`
I really like this approach! But shouldn't this behavior also apply to memref?
What I don't understand is whether it makes sense to have two different behaviors for `memref.load` and `vector.load`.
Additionally, `nneg` is solely for indices, it's not imply `nuw`. Basically, "`nuw`" == "`nneg` with non-negative stride". Should we cover this thing?
> 3. Last I checked, LLVM _doesn't_ produce poison if you index outside of an object with a non-`inbounds` GEP, hence the flag? Like, I'm pretty sure you're allowed to do
>
> ```llvm
> ;; %x points to a [2 x i32]
> ;; %y is a valid pointer to the end of %x, as in .end() for array iterators in C++
> %y = getelementptr i32, ptr %x, i64 3
> ;; this is probably UB, or at least target-defined
> %l = load i32, ptr %y
> ```
>
> So you only get immediate `poison` if you mark the GEP `inbounds`.
>
> (Now, for buffer fat pointers, everything works out. If you `inbounds` your pointer, you're promising you're in the logical object pointer pointed to by the resource. If you don't, you're off the edge of the object, and target-specific documentation says that going off the end means reading 0 / dropping writes (maybe not super-clearly but), which is a perfectly correct refinement of LLVM UB.
Exactly. You're right. Poison values only occur when an attribute is violated (approximately). Since the GEP does't acces memory your GEP is free from poisons.
To answer this question (UB or target-defined?), which does not seem to be clarified by the llvm.load documentation alone
```llvm
%l = load i32, ptr %y
```
the [Pointer Aliasing Rules](https://llvm.org/docs/LangRef.html#pointer-aliasing-rules) say: "any memory access must be done through a pointer value associated with an address range of the memory access, otherwise the behavior is undefined", hence I'd say UB confirmed.
> Also, the OOB tail is LLVM-level UB, but, again, in graphics contexts or other such cases, you can absolutely have _defined_ behavior for such a tail. It's perhaps more correct to say that OOB tails, OOB start indices, etc. have _implementation-defined_ behavior at the MLIR level. If you want to explicitly exclude the possibility, you should put a flag on.
That's true as well. I really like to leave the choice of including attributes or not to the specific lowering.
https://github.com/llvm/llvm-project/pull/202118
More information about the Mlir-commits
mailing list