llvm / llvm/llvm-project

[AArch64] Materialise constants in NEON registers using `MOVI`/`MVNI`

Open
#182,427 3 comments 0 reactions 1 assignee View on GitHub

@HerrCai0907 is already working on this.

Since Feb 20, 2026.

backend:AArch64 missed-optimization
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

When materialising a constant in a NEON register, LLVM prefers to first materialise the constant into a scalar register, then copy into a NEON register using `FMOV`. However, in some cases, LLVM could use [`MOVI`](https://docsmirror.github.io/A64/2023-06/movi_advsimd.html) or [`MVNI` ](https://docsmirror.github.io/A64/2023-06/mvni_advsimd.html)

# Example
https://godbolt.org/z/6vG1Kn15E
```c++
#include

#include

using half = __fp16;

half half_movi_1() { return std::bit_cast(1); }
half half_movi_2() { return std::bit_cast(2); }
half half_movi_3() { return std::bit_cast(3); }

half half_mvni_0() { return std::bit_cast(-1); }
half half_mvni_1() { return std::bit_cast(-2); }
half half_mvni_2() { return std::bit_cast(-3); }

float float_movi_1() { return std::bit_cast(1); }
float float_movi_2() { return std::bit_cast(2); }
float float_movi_3() { return std::bit_cast(3); }

float float_mvni_0() { return std::bit_cast(-1); }
float float_mvni_1() { return std::bit_cast(-2); }
float float_mvni_2() { return std::bit_cast(-3); }

double double_mvni_0() { return std::bit_cast(0xffffffff'ffffffff); }
double double_mvni_1() { return std::bit_cast(0xfffffffe'fffffffe); }
double double_mvni_2() { return std::bit_cast(0xfffffffd'fffffffd); }
```

## GCC assembly
```asm
half_movi_1():
movi v0.4h, 0x1
ret
half_movi_2():
movi v0.4h, 0x2
ret
half_movi_3():
movi v0.4h, 0x3
ret
half_mvni_0():
mvni v0.2s, 0
ret
half_mvni_1():
mvni v0.4h, 0x1
ret
half_mvni_2():
mvni v0.4h, 0x2
ret
float_movi_1():
movi v0.2s, 0x1
ret
float_movi_2():
movi v0.2s, 0x2
ret
float_movi_3():
movi v0.2s, 0x3
ret
float_mvni_0():
mvni v0.2s, 0
ret
float_mvni_1():
mvni v0.2s, 0x1
ret
float_mvni_2():
mvni v0.2s, 0x2
ret
double_mvni_0():
mvni v0.4s, 0
ret
double_mvni_1():
mvni v0.4s, 0x1
ret
double_mvni_2():
mvni v0.4s, 0x2
ret
double double_mvni_2() { return std::bit_cast(0xfffffffd'fffffffd); }
```

## LLVM assembly
```asm
.LCPI0_0:
.hword 0x0001
half_movi_1():
adrp x8, .LCPI0_0
ldr h0, [x8, :lo12:.LCPI0_0]
ret

.LCPI1_0:
.hword 0x0002
half_movi_2():
adrp x8, .LCPI1_0
ldr h0, [x8, :lo12:.LCPI1_0]
ret

.LCPI2_0:
.hword 0x0003
half_movi_3():
adrp x8, .LCPI2_0
ldr h0, [x8, :lo12:.LCPI2_0]
ret

.LCPI3_0:
.hword 0xffff
half_mvni_0():
adrp x8, .LCPI3_0
ldr h0, [x8, :lo12:.LCPI3_0]
ret

.LCPI4_0:
.hword 0xfffe
half_mvni_1():
adrp x8, .LCPI4_0
ldr h0, [x8, :lo12:.LCPI4_0]
ret

.LCPI5_0:
.hword 0xfffd
half_mvni_2():
adrp x8, .LCPI5_0
ldr h0, [x8, :lo12:.LCPI5_0]
ret

float_movi_1():
mov w8, #1
fmov s0, w8
ret

float_movi_2():
mov w8, #2
fmov s0, w8
ret

float_movi_3():
mov w8, #3
fmov s0, w8
ret

float_mvni_0():
mov w8, #-1
fmov s0, w8
ret

float_mvni_1():
mov w8, #-2
fmov s0, w8
ret

float_mvni_2():
mov w8, #-3
fmov s0, w8
ret

double_mvni_0():
movi d0, #0xffffffffffffffff
ret

double_mvni_1():
mov x8, #-4294967298
fmov d0, x8
ret

double_mvni_2():
mov x8, #-8589934595
fmov d0, x8
ret
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.