Summary
For, seemingly, all Arm left shift neon intrinsics that accept as a first argument a vector and a constant integer as a second argument, the second argument gets turned into a const vector.
This is problematic for languages that wish to use the intrinsics but do not natively support const vectors. For example they needed to be implemented in Rust.
How to reproduce:
#include <stdio.h>
#include <stdint.h>
#ifdef __ARM_NEON
#include <arm_neon.h>
#else
#error "arm only"
#endif
int
main(void)
{
#define N 1
int8x8_t a = {15, 14, 13, 12, 11, 10, 9, 8};
/* Example */
uint8x8_t retval = vqshlu_n_s8(a, N);
for (int i = 0; i < 8; ++i) {
printf("[%d] => %d\n", i, retval[i]);
}
return 0;
}
According to the documentation it should produce something like the following ir/llvm call:
<8 x i8> @llvm.aarch64.neon.sqshlu.v8i8(<8 x i8>, i32)
However something is wrong meaning it actually expects a constant vector as it's second argument like so:
<8 x i8> @llvm.aarch64.neon.sqshlu.v8i8(<8 x i8>, <8 x i8>)
However the resulting Arm assembly does produce the correct call:
sqshlu v0.8b, v0.8b, #1
If you supply something like the following as a second argument in the IR the llc will crash as all values need to be the same
Exploration
This seems to be caused by the incorrect type being used in the tablegen file: llvm/include/llvm/IR/IntrinsicsAArch64.td. For example the sqshlu intrinsic is using the type AdvSIMD_2IntArg_Intrinsic which, through me fumbling around, seems to be remedied with the use of a type like the bellow along with removing the intrinsic out of the NEONMAP in clang/lib/CodeGen/CGBuiltin.cpp however I was mostly looking at the IR as opposed to clang and this is a fairly surface level dive into the problem
/* Example type that seemed to work */
class AdvSIMD_2VectorArg_Scalar
: DefaultAttrsIntrinsic<[llvm_anyint_ty],
[LLVMMatchType<0>, llvm_i32_ty],
[IntrNoMem]>;
**Summary**
For, seemingly, all Arm left shift neon intrinsics that accept as a first argument a vector and a constant integer as a second argument, the second argument gets turned into a const vector.
This is problematic for languages that wish to use the intrinsics but do not natively support const vectors. For example they needed to be [implemented in Rust](https://github.com/rust-lang/rust/issues/118209).
**How to reproduce:**
```c
#include <stdio.h>
#include <stdint.h>
#ifdef __ARM_NEON
#include <arm_neon.h>
#else
#error "arm only"
#endif
int
main(void)
{
#define N 1
int8x8_t a = {15, 14, 13, 12, 11, 10, 9, 8};
/* Example */
uint8x8_t retval = vqshlu_n_s8(a, N);
for (int i = 0; i < 8; ++i) {
printf("[%d] => %d\n", i, retval[i]);
}
return 0;
}
```
According to the [documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqshlu_n_s8) it should produce something like the following ir/llvm call:
```llvm
<8 x i8> @llvm.aarch64.neon.sqshlu.v8i8(<8 x i8>, i32)
```
However something is wrong meaning it actually expects a constant vector as it's second argument like so:
```llvm
<8 x i8> @llvm.aarch64.neon.sqshlu.v8i8(<8 x i8>, <8 x i8>)
```
However the resulting Arm assembly does produce the correct call:
```asm
sqshlu v0.8b, v0.8b, #1
```
If you supply something like the following as a second argument in the IR the `llc` will crash as all values need to be the same
```llvm
call @llvm.aarch64.neon.sqshlu.v8i8(
<8 x i8> <i8 15, i8 14, i8 13, i8 12, i8 11, i8 10, i8 9, i8 8>
<8 x i8> <i8 1, i8 2, i8, 3, i8 4, i8 5, i8 6, i8 7, i8 8>
)
```
As it is unable to create the correct Arm assembly code.
This observation is the same for a large portion, if not all, neon intrinsics requiring a left shift by a constant immediate value.
The intrinsics I have observed this with but are potentially not limited to:
- `vqshlu_n_<size>`
- `vqshl_n_<size>`
- `vqshlb_n_<size>`
- `vqshlq_n_<size>`
(where `<size>` denotes `s8`, `u8` etc...)
**Exploration**
This seems to be caused by the incorrect type being used in the tablegen file: `llvm/include/llvm/IR/IntrinsicsAArch64.td`. For example the `sqshlu` intrinsic is using the type `AdvSIMD_2IntArg_Intrinsic` which, through me fumbling around, seems to be remedied with the use of a type like the bellow along with removing the intrinsic out of the NEONMAP in `clang/lib/CodeGen/CGBuiltin.cpp` however I was mostly looking at the IR as opposed to clang and this is a fairly surface level dive into the problem
```
/* Example type that seemed to work */
class AdvSIMD_2VectorArg_Scalar
: DefaultAttrsIntrinsic<[llvm_anyint_ty],
[LLVMMatchType<0>, llvm_i32_ty],
[IntrNoMem]>;
```
Summary For, seemingly, all Arm left shift neon intrinsics that accept as a first argument a vector and a constant integer as a second argument, the second argument gets turned into a const vector.
This is problematic for languages that wish to use the intrinsics but do not natively support const vectors. For example they needed to be implemented in Rust.
How to reproduce:
According to the documentation it should produce something like the following ir/llvm call:
However something is wrong meaning it actually expects a constant vector as it's second argument like so:
However the resulting Arm assembly does produce the correct call:
If you supply something like the following as a second argument in the IR the
llc
will crash as all values need to be the sameAs it is unable to create the correct Arm assembly code.
This observation is the same for a large portion, if not all, neon intrinsics requiring a left shift by a constant immediate value.
The intrinsics I have observed this with but are potentially not limited to:
vqshlu_n_<size>
vqshl_n_<size>
vqshlb_n_<size>
vqshlq_n_<size>
(where<size>
denotess8
,u8
etc...)Exploration This seems to be caused by the incorrect type being used in the tablegen file:
llvm/include/llvm/IR/IntrinsicsAArch64.td
. For example thesqshlu
intrinsic is using the typeAdvSIMD_2IntArg_Intrinsic
which, through me fumbling around, seems to be remedied with the use of a type like the bellow along with removing the intrinsic out of the NEONMAP inclang/lib/CodeGen/CGBuiltin.cpp
however I was mostly looking at the IR as opposed to clang and this is a fairly surface level dive into the problem