移动指令主要涉及 MOV 和 MVN,它们分别是移动和求反移动。如果你认为仅仅两条指令,还是太小看设计者了!
1 MOV (element)
将向量元素移动到另一个向量元素。该指令将源 SIMD&FP 寄存器的向量元素复制到目标 SIMD&FP 寄存器的指定向量元素。该指令可以将数据插入 SIMD&FP 寄存器中的各个元素,而无需将剩余位清零。
MOV <Vd>.<Ts>[<index1>], <Vn>.<Ts>[<index2>]
等价指令
INS <Vd>.<Ts>[<index1>], <Vn>.<Ts>[<index2>]
<Vd>
是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。
<Ts>
是元素大小说明符,编码为“imm5”:
imm5 | <Ts> |
---|---|
x0000 | RESERVED |
xxxx1 | B |
xxx10 | H |
xx100 | S |
x1000 | D |
<index1>
是以“imm5”编码的目标元素索引:
imm5 | <index1> |
---|---|
x0000 | RESERVED |
xxxx1 | imm5<4:1> |
xxx10 | imm5<4:2> |
xx100 | imm5<4:3> |
x1000 | imm5<4> |
<Vn>
是 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。
<index2>
是以“imm5:imm4”编码的源元素索引:
imm5 | <index2> |
---|---|
x0000 | RESERVED |
xxxx1 | imm4<3:0> |
xxx10 | imm4<3:1> |
xx100 | imm4<3:2> |
x1000 | imm4<3> |
“imm4”中未指定的位将被忽略,但应由汇编程序设置为零。
下面是使用 MOV (element) 指令的例子。
int len = 4;
auto *src = new unsigned int[len];
auto *dst = new unsigned int[len]{0};
for (int i = 0; i < len; i++) {
src[i] = (i + 1) * 0x11111111;
}
LOGD("in src: src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x", 0, src[0], 1, src[1],
2, src[2], 3, src[3]);
LOGD("in dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
asm volatile(
"LD1 {v0.4S}, [%[src]]\n"
"LD1 {v1.4S}, [%[dst]]\n"
"MOV v1.S[0], v0.S[3]\n"
"ST1 {v1.4S}, [%[dst]]\n"
:[src] "+r"(src),
[dst] "+r"(dst)
:
: "cc", "memory", "v0", "v1");
LOGD("-----------------------------");
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
delete[] src;
delete[] dst;
MOV v1.S[0], v0.S[3]
将 v0 S 第四通道的值移动到 v1 S 第一通道,v1 其他 S 通道的值保持不变。
运行结果:
2023-06-07 07:55:22.331 22545-22629/com.demo.myapplication D/NativeCore: in src: src[0]=0x11111111 src[1]=0x22222222 src[2]=0x33333333 src[3]=0x44444444
2023-06-07 07:55:22.331 22545-22629/com.demo.myapplication D/NativeCore: in dst: dst[0]=0x0 dst[1]=0x0 dst[2]=0x0 dst[3]=0x0
2023-06-07 07:55:22.331 22545-22629/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-07 07:55:22.331 22545-22629/com.demo.myapplication D/NativeCore: out dst: dst[0]=0x44444444 dst[1]=0x0 dst[2]=0x0 dst[3]=0x0
2 MOV (from general)
将通用寄存器移动到向量元素。该指令将源通用寄存器的内容复制到目标 SIMD&FP 寄存器中指定的向量元素。该指令可以将数据插入 SIMD&FP 寄存器中的各个元素,而无需将剩余位清零。
MOV <Vd>.<Ts>[<index>], <R><n>
等价指令
INS <Vd>.<Ts>[<index>], <R><n>
<Vd>
是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。
<Ts>
是元素大小说明符,编码为“imm5”:
imm5 | <Ts> |
---|---|
x0000 | RESERVED |
xxxx1 | B |
xxx10 | H |
xx100 | S |
x1000 | D |
<index>
是以“imm5”编码的目标元素索引:
imm5 | <index> |
---|---|
x0000 | RESERVED |
xxxx1 | imm5<4:1> |
xxx10 | imm5<4:2> |
xx100 | imm5<4:3> |
x1000 | imm5<4> |
<R>
是通用源寄存器的宽度说明符,编码为“imm5”:
imm5 | <R> |
---|---|
x0000 | RESERVED |
xxxx1 | W |
xxx10 | W |
xx100 | W |
x1000 | X |
<n>
是通用源寄存器或 ZR (31) 的编号 [0-30],编码在“Rn”字段中。
下面是使用 MOV (from general) 指令的例子。
int len = 4;
auto *dst = new unsigned int[len]{0};
unsigned int src = 0x12345678;
LOGD("in src: 0x%x ", src);
LOGD("in dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
asm volatile(
"LD1 {v1.4S}, [%[dst]]\n"
"MOV v1.S[0], %w[src]\n"
"ST1 {v1.4S}, [%[dst]]\n"
:[src] "+r"(src),
[dst] "+r"(dst)
:
: "cc", "memory", "v1");
LOGD("-----------------------------");
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
delete[] dst;
MOV v1.S[0], %w[src]
将通用 W 寄存器的值移动到 v1 S 第一通道,v1 其他 S 通道的值保持不变。
运行结果:
2023-06-07 08:20:59.353 20134-20205/com.demo.myapplication D/NativeCore: in src: 0x12345678
2023-06-07 08:20:59.353 20134-20205/com.demo.myapplication D/NativeCore: in dst: dst[0]=0x0 dst[1]=0x0 dst[2]=0x0 dst[3]=0x0
2023-06-07 08:20:59.353 20134-20205/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-07 08:20:59.353 20134-20205/com.demo.myapplication D/NativeCore: out dst: dst[0]=0x12345678 dst[1]=0x0 dst[2]=0x0 dst[3]=0x0
3 MOV (scalar)
将向量元素移动到标量。该指令将 SIMD&FP 源寄存器中的指定向量元素复制为标量,并将结果写入 SIMD&FP 目标寄存器。
MOV <V><d>, <Vn>.<T>[<index>]
等价指令
DUP <V><d>, <Vn>.<T>[<index>]
<V>
是目标宽度说明符,编码为“imm5”:
imm5 | <V> |
---|---|
x0000 | RESERVED |
xxxx1 | B |
xxx10 | H |
xx100 | S |
x1000 | D |
<d>
是 SIMD&FP 目标寄存器的编号,编码在“Rd”字段中。
<Vn>
是 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。
<T>
是元素宽度说明符,编码为“imm5”:
imm5 | <T> |
---|---|
x0000 | RESERVED |
xxxx1 | B |
xxx10 | H |
xx100 | S |
x1000 | D |
<index>
是“imm5”中编码的元素索引:
imm5 | <index> |
---|---|
x0000 | RESERVED |
xxxx1 | imm5<4:1> |
xxx10 | imm5<4:2> |
xx100 | imm5<4:3> |
x1000 | imm5<4> |
下面是使用 MOV (scalar) 指令的例子。
int len = 4;
auto *src = new unsigned int[len];
auto *dst = new unsigned int[len]{0};
for (int i = 0; i < len; i++) {
src[i] = 0x11111111 * (i + 1);
}
LOGD("in src: src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x", 0, src[0], 1, src[1],
2, src[2], 3, src[3]);
LOGD("in dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
asm volatile(
"LD1 {v0.4S}, [%[src]]\n"
"LD1 {v1.4S}, [%[dst]]\n"
"MOV S1, v0.S[3]\n"
"ST1 {v1.4S}, [%[dst]]\n"
:[src] "+r"(src),
[dst] "+r"(dst)
:
: "cc", "memory", "v1");
LOGD("-----------------------------");
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
delete[] src;
delete[] dst;
MOV S1, v0.S[3]
将 v0 S 第四通道值移动到标量 S1(映射为 v1 寄存器的最低 S 部分(第一 S 通道))。
运行结果:
2023-06-07 08:35:21.193 30974-31058/com.demo.myapplication D/NativeCore: in src: src[0]=0x11111111 src[1]=0x22222222 src[2]=0x33333333 src[3]=0x44444444
2023-06-07 08:35:21.193 30974-31058/com.demo.myapplication D/NativeCore: in dst: dst[0]=0x0 dst[1]=0x0 dst[2]=0x0 dst[3]=0x0
2023-06-07 08:35:21.193 30974-31058/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-07 08:35:21.193 30974-31058/com.demo.myapplication D/NativeCore: out dst: dst[0]=0x44444444 dst[1]=0x0 dst[2]=0x0 dst[3]=0x0
4 MOV (to general)
将向量元素移动到通用寄存器。该指令从源 SIMD&FP 寄存器中读取无符号整数,对其进行零扩展以形成 32 位或 64 位值,并将结果写入目标通用寄存器。
32-bit (Q == 0 && imm5 == xx100)
MOV <Wd>, <Vn>.S[<index>]
等价指令
UMOV <Wd>, <Vn>.S[<index>]
64-bit (Q == 1 && imm5 == x1000)
MOV <Xd>, <Vn>.D[<index>]
等价指令
UMOV <Xd>, <Vn>.D[<index>]
<Wd>
是通用目标寄存器的 32 位名称,在“Rd”字段中编码。
<Xd>
是通用目标寄存器的 64 位名称,在“Rd”字段中编码。
<Vn>
是 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。
<index>
对于 32 位变体:是在“imm5<4:3>”中编码的元素索引。对于 64 位变体:是在“imm5<4>”中编码的元素索引。
下面是使用 MOV (to general) 指令的例子。
int len = 4;
auto *src = new unsigned int[len];
unsigned int dst = 0;
for (int i = 0; i < len; i++) {
src[i] = 0x11111111 * (i + 1);
}
LOGD("in src: src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x", 0, src[0], 1, src[1],
2, src[2], 3, src[3]);
asm volatile(
"LD1 {v0.4S}, [%[src]]\n"
"MOV %w[dst], v0.S[3]\n"
:[src] "+r"(src),
[dst] "+r"(dst)
:
: "cc", "memory", "v1");
LOGD("-----------------------------");
LOGD("out dst: 0x%x", dst);
delete[] src;
MOV %w[dst], v0.S[3]
将 v0 S 第四通道值移动到通用寄存器。
运行结果:
2023-06-08 07:46:51.911 3993-4135/com.demo.myapplication D/NativeCore: in src: src[0]=0x11111111 src[1]=0x22222222 src[2]=0x33333333 src[3]=0x44444444
2023-06-08 07:46:51.911 3993-4135/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-08 07:46:51.911 3993-4135/com.demo.myapplication D/NativeCore: out dst: 0x44444444
5 MOV (vector)
移动向量。该指令将源 SIMD&FP 寄存器中的向量复制到目标 SIMD&FP 寄存器中。
MOV <Vd>.<T>, <Vn>.<T>
等价指令
ORR <Vd>.<T>, <Vn>.<T>, <Vn>.<T>
<Vd>
是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。
<T>
是排列说明符,用“Q”编码:
Q | <T> |
---|---|
0 | 8B |
1 | 16B |
<Vn>
是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。
下面是使用 MOV (vector) 指令的例子。
int len = 4;
auto *src = new unsigned int[len];
auto *dst = new unsigned int[len]{0};
for (int i = 0; i < len; i++) {
src[i] = 0x11111111 * (i + 1);
}
LOGD("in src: src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x src[%d]=0x%x", 0, src[0], 1, src[1],
2, src[2], 3, src[3]);
LOGD("in dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
asm volatile(
"LD1 {v0.4S}, [%[src]]\n"
"LD1 {v1.4S}, [%[dst]]\n"
"MOV v1.16B, v0.16B\n"
"ST1 {v1.4S}, [%[dst]]\n"
:[src] "+r"(src),
[dst] "+r"(dst)
:
: "cc", "memory", "v1");
LOGD("-----------------------------");
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
delete[] src;
delete[] dst;
MOV v1.16B, v0.16B
将 v0 16 个 B 通道的值移动到对应的 v1 16 个 B 通道。
运行结果:
2023-06-08 07:54:34.443 8130-8174/com.demo.myapplication D/NativeCore: in src: src[0]=0x11111111 src[1]=0x22222222 src[2]=0x33333333 src[3]=0x44444444
2023-06-08 07:54:34.443 8130-8174/com.demo.myapplication D/NativeCore: in dst: dst[0]=0x0 dst[1]=0x0 dst[2]=0x0 dst[3]=0x0
2023-06-08 07:54:34.443 8130-8174/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-08 07:54:34.443 8130-8174/com.demo.myapplication D/NativeCore: out dst: dst[0]=0x11111111 dst[1]=0x22222222 dst[2]=0x33333333 dst[3]=0x44444444
6 MOVI
移动立即数(向量)。该指令将一个立即数放入目标 SIMD&FP 寄存器的每个向量元素中。
8-bit (op == 0 && cmode == 1110)
MOVI <Vd>.<T>, #<imm8>{, LSL #0}
16-bit shifted immediate (op == 0 && cmode == 10x0)
MOVI <Vd>.<T>, #<imm8>{, LSL #<amount>}
32-bit shifted immediate (op == 0 && cmode == 0xx0)
MOVI <Vd>.<T>, #<imm8>{, LSL #<amount>}
32-bit shifting ones (op == 0 && cmode == 110x)
MOVI <Vd>.<T>, #<imm8>, MSL #<amount>
64-bit scalar (Q == 0 && op == 1 && cmode == 1110)
MOVI <Dd>, #<imm>
64-bit vector (Q == 1 && op == 1 && cmode == 1110)
MOVI <Vd>.2D, #<imm>
<Dd>
是 SIMD&FP 目标寄存器的 64 位名称,在“Rd”字段中编码。
<Vd>
是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。
<imm>
是一个 64 位立即数 ‘aaaaaaaabbbbbbbbbccccccccdddddddddeeeeeeeeeffffffffggggggghhhhhhhh’,
编码为“a🅱️c:d:e:f:g:h”。
<T>
是排列说明符,以“Q”编码。
对于 8 位变体:
Q | <T> |
---|---|
0 | 8B |
1 | 16B |
对于 16 位变体:
Q | <T> |
---|---|
0 | 4H |
1 | 8H |
对于 32 位变体:
Q | <T> |
---|---|
0 | 2S |
1 | 4S |
<imm8>
是以“a🅱️c:d:e:f:g:h”编码的 8 位立即数。
<amount>
对于 16 位移位立即数变体:是在“cmode<1>”中编码的移位量:
cmode<1> | <amount> |
---|---|
0 | 0 |
1 | 8 |
如果省略 LSL,则默认为 0。
对于 32 位移位立即数变体:是在“cmode<2:1>”中编码的移位量:
cmode<2:1> | <amount> |
---|---|
00 | 0 |
01 | 8 |
10 | 16 |
11 | 24 |
如果省略 LSL,则默认为 0。
对于 32 位移位变体:是编码在“cmode<0>”中的移位量:
cmode<0> | <amount> |
---|---|
0 | 8 |
1 | 16 |
下面是使用 MOVI 指令的例子。
int len = 16;
auto *dst = new char[len]{0};
auto *dst1 = new unsigned int[4]{0};
asm volatile(
"MOVI v0.16B, #0x12\n"
"MOVI v1.4S, #0x34\n"
"ST1 {v0.16B}, [%[dst]]\n"
"ST1 {v1.4S}, [%[dst1]]\n"
:[dst] "+r"(dst),
[dst1] "+r"(dst1)
:
: "cc", "memory", "v0", "v1");
LOGD("-----------------------------");
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 4, dst[4], 5, dst[5],
6, dst[6], 7, dst[7]);
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 8, dst[8], 9, dst[9],
10, dst[10], 11, dst[11]);
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 12, dst[12], 13, dst[13],
14, dst[14], 15, dst[15]);
LOGD("out dst1: dst1[%d]=0x%x dst1[%d]=0x%x dst1[%d]=0x%x dst1[%d]=0x%x ", 0, dst1[0], 1,
dst1[1], 2, dst1[2], 3, dst1[3]);
delete[] dst;
delete[] dst1;
MOVI v0.16B, #0x12
将立即数 0x12 移动到 v0 16 个 B 通道,MOVI v1.4S, #0x34
将立即数 0x34 移动到 v1 4 个 S 通道。
运行结果:
2023-06-08 08:42:46.982 27099-27138/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-08 08:42:46.982 27099-27138/com.demo.myapplication D/NativeCore: out dst: dst[0]=0x12 dst[1]=0x12 dst[2]=0x12 dst[3]=0x12
2023-06-08 08:42:46.982 27099-27138/com.demo.myapplication D/NativeCore: out dst: dst[4]=0x12 dst[5]=0x12 dst[6]=0x12 dst[7]=0x12
2023-06-08 08:42:46.982 27099-27138/com.demo.myapplication D/NativeCore: out dst: dst[8]=0x12 dst[9]=0x12 dst[10]=0x12 dst[11]=0x12
2023-06-08 08:42:46.982 27099-27138/com.demo.myapplication D/NativeCore: out dst: dst[12]=0x12 dst[13]=0x12 dst[14]=0x12 dst[15]=0x12
2023-06-08 08:42:46.982 27099-27138/com.demo.myapplication D/NativeCore: out dst1: dst1[0]=0x34 dst1[1]=0x34 dst1[2]=0x34 dst1[3]=0x34
7 MVN
按位 NOT(向量),该指令从源 SIMD&FP 寄存器中读取每个向量元素,将每个值的反码放入向量中,然后将向量写入目标 SIMD&FP 寄存器。
MVN <Vd>.<T>, <Vn>.<T>
等价指令
NOT <Vd>.<T>, <Vn>.<T>
<Vd>
是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。
<T>
是排列说明符,用“Q”编码:
Q | <T> |
---|---|
0 | 8B |
1 | 16B |
<Vn>
是 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。
下面是使用 MVN 指令的例子。
int len = 8;
auto *dst = new char[len];
for(int i = 0; i < len; i++){
dst[i] = i;
}
LOGD("in dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
LOGD("in dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 4, dst[4], 5, dst[5],
6, dst[6], 7, dst[7]);
asm volatile(
"LD1 {v0.8B}, [%[dst]]\n"
"MVN v1.8B, v0.8B\n"
"ST1 {v1.8B}, [%[dst]]\n"
:[dst] "+r"(dst)
:
: "cc", "memory", "v0", "v1");
LOGD("-----------------------------");
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 4, dst[4], 5, dst[5],
6, dst[6], 7, dst[7]);
delete[] dst;
MVN v1.8B, v0.8B
将 v0 寄存器 8 个 B 通道的值取反码放入 v1 寄存器 8 个 B 通道。
运行结果:
2023-06-09 07:59:45.055 21805-21871/com.demo.myapplication D/NativeCore: in dst: dst[0]=0x0 dst[1]=0x1 dst[2]=0x2 dst[3]=0x3
2023-06-09 07:59:45.055 21805-21871/com.demo.myapplication D/NativeCore: in dst: dst[4]=0x4 dst[5]=0x5 dst[6]=0x6 dst[7]=0x7
2023-06-09 07:59:45.055 21805-21871/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-09 07:59:45.055 21805-21871/com.demo.myapplication D/NativeCore: out dst: dst[0]=0xff dst[1]=0xfe dst[2]=0xfd dst[3]=0xfc
2023-06-09 07:59:45.055 21805-21871/com.demo.myapplication D/NativeCore: out dst: dst[4]=0xfb dst[5]=0xfa dst[6]=0xf9 dst[7]=0xf8
8 MVNI
移动取反立即数(向量),该指令将立即数的反码放入目标 SIMD&FP 寄存器的每个向量元素中。
16-bit shifted immediate (cmode == 10x0)
MVNI <Vd>.<T>, #<imm8>{, LSL #<amount>}
32-bit shifted immediate (cmode == 0xx0)
MVNI <Vd>.<T>, #<imm8>{, LSL #<amount>}
32-bit shifting ones (cmode == 110x)
MVNI <Vd>.<T>, #<imm8>, MSL #<amount>
<Vd>
是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。
<T>
是排列说明符,以“Q”编码。
对于 16 位变体:
Q | <T> |
---|---|
0 | 4H |
1 | 8H |
对于 32 位变体:
Q | <T> |
---|---|
0 | 2S |
1 | 4S |
<imm8>
是以“a🅱️c:d:e:f:g:h”编码的 8 位立即数。
<amount>
对于 16 位移位立即数变体:是在“cmode<1>”中编码的移位量:
cmode<1> | <amount> |
---|---|
0 | 0 |
1 | 8 |
如果省略 LSL,则默认为 0。
对于 32 位移位立即数变体:是在“cmode<2:1>”中编码的移位量:
cmode<2:1> | <amount> |
---|---|
00 | 0 |
01 | 8 |
10 | 16 |
11 | 24 |
如果省略 LSL,则默认为 0。
对于 32 位移位变体:是编码在“cmode<0>”中的移位量:
cmode<0> | <amount> |
---|---|
0 | 8 |
1 | 16 |
下面是使用 MVNI 指令的例子。
int len = 4;
auto *dst = new unsigned int[len]{0};
asm volatile(
"MVNI v0.4S, #0x7, LSL#8\n"
"ST1 {v0.4S}, [%[dst]]\n"
:[dst] "+r"(dst)
:
: "cc", "memory", "v0");
LOGD("-----------------------------");
LOGD("out dst: dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x dst[%d]=0x%x", 0, dst[0], 1, dst[1],
2, dst[2], 3, dst[3]);
delete[] dst;
MVNI v0.4S, #0x7, LSL#8
将立即数 0x7 逻辑左移 8 位,即变为 0x700,接着按位取反即 0xfffff8ff,最终将 0xfffff8ff 移动到 v0 寄存器 4 个 S 通道。
运行结果:
2023-06-09 08:15:31.531 31584-31656/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-09 08:15:31.531 31584-31656/com.demo.myapplication D/NativeCore: out dst: dst[0]=0xfffff8ff dst[1]=0xfffff8ff dst[2]=0xfffff8ff dst[3]=0xfffff8ff
9 SMOV
有符号移动向量元素到通用寄存器。该指令从源 SIMD&FP 寄存器中读取有符号整数,对其进行符号扩展以形成 32 位或 64 位值,并将结果写入目标通用寄存器。
32-bit (Q == 0)
SMOV <Wd>, <Vn>.<Ts>[<index>]
64-bit (Q == 1)
SMOV <Xd>, <Vn>.<Ts>[<index>]
<Wd>
是通用目标寄存器的 32 位名称,在“Rd”字段中编码。
<Xd>
是通用目标寄存器的 64 位名称,在“Rd”字段中编码。
<Vn>
是 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。
<Ts>
是一个元素大小说明符,以“imm5”编码:
对于 32 位变体:
imm5 | <Ts> |
---|---|
xxx00 | RESERVED |
xxxx1 | B |
xxx10 | H |
对于 64 位变体:
imm5 | <Ts> |
---|---|
xx000 | RESERVED |
xxxx1 | B |
xxx10 | H |
xx100 | S |
<index>
是在“imm5”中编码的元素索引:
对于 32 位变体:
imm5 | <index> |
---|---|
xxx00 | RESERVED |
xxxx1 | imm5<4:1> |
xxx10 | imm5<4:2> |
对于 64 位变体:
imm5 | <index> |
---|---|
xx000 | RESERVED |
xxxx1 | imm5<4:1> |
xxx10 | imm5<4:2> |
xx100 | imm5<4:3> |
下面是使用 SMOV 指令的例子。
int len = 4;
auto *src = new int[len];
long long int dst = 0;
for (int i = 0; i < len; i++) {
src[i] = -100 * (i + 1);
}
LOGD("in src: src[%d]=%d src[%d]=%d src[%d]=%d src[%d]=%d", 0, src[0], 1, src[1],
2, src[2], 3, src[3]);
asm volatile(
"LD1 {v0.4S}, [%[src]]\n"
"SMOV %[dst], v0.S[3]\n"
:[src] "+r"(src),
[dst] "+r"(dst)
:
: "cc", "memory", "v0");
LOGD("-----------------------------");
LOGD("out dst: %lld", dst);
delete[] src;
SMOV %[dst], v0.S[3]
将 v0 寄存器第四个 S 通道的值移动到通用 X 寄存器(对其进行符号扩展)。
运行结果:
2023-06-09 08:31:27.692 19705-19770/com.demo.myapplication D/NativeCore: in src: src[0]=-100 src[1]=-200 src[2]=-300 src[3]=-400
2023-06-09 08:31:27.692 19705-19770/com.demo.myapplication D/NativeCore: -----------------------------
2023-06-09 08:31:27.692 19705-19770/com.demo.myapplication D/NativeCore: out dst: -400
10 UMOV
无符号移动向量元素到通用寄存器,该指令从源 SIMD&FP 寄存器中读取无符号整数,对其进行零扩展以形成 32 位或 64 位值,并将结果写入目标通用寄存器。
32-bit (Q == 0)
UMOV <Wd>, <Vn>.<Ts>[<index>]
64-bit (Q == 1 && imm5 == x1000)
UMOV <Xd>, <Vn>.<Ts>[<index>]
<Xd>
是通用目标寄存器的 64 位名称,在“Rd”字段中编码。
<Vn>
是 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。
<Ts>
是元素大小说明符,编码在“imm5”字段中。
对于 32 位变体:
imm5 | <Ts> |
---|---|
B | xxxx1 |
H | xxx10 |
S | xx100 |
RESERVED | xx000 |
对于 64 位变体:
imm5 | <Ts> |
---|---|
D | x1000 |
RESERVED | x0000 |
RESERVED | xxxx1 |
RESERVED | xxx10 |
RESERVED | xx100 |
<index>
对于 32 位变体:是在“imm5”字段中编码的元素索引。
imm5 | <index> |
---|---|
xx000 | RESERVED |
xxxx1 | imm5<4:1> |
xxx10 | imm5<4:2> |
xx100 | imm5<4:3> |
对于 64 位变体:是在“imm5<4>”中编码的元素索引。
下面是使用 UMOV 指令的例子。
int len = 4;
auto *src = new unsigned int[len];
unsigned int dst = 0;
for (int i = 0; i < len; i++) {
src[i] = 100 * (i + 1);
}
LOGD("in src: src[%d]=%u src[%d]=%u src[%d]=%u src[%d]=%u", 0, src[0], 1, src[1],
2, src[2], 3, src[3]);
asm volatile(
"LD1 {v0.4S}, [%[src]]\n"
"UMOV %w[dst], v0.S[3]\n"
:[src] "+r"(src),
[dst] "+r"(dst)
:
: "cc", "memory", "v0");
LOGD("-----------------------------");
LOGD("out dst: %u", dst);
delete[] src;
UMOV %[dst], v0.S[3]
将 v0 寄存器第四个 S 通道的值移动到通用 W 寄存器。
运行结果:
2023-06-10 08:06:12.981 20649-20649/com.example.myapplication D/native-armv8a: in src: src[0]=100 src[1]=200 src[2]=300 src[3]=400
2023-06-10 08:06:12.981 20649-20649/com.example.myapplication D/native-armv8a: -----------------------------
2023-06-10 08:06:12.981 20649-20649/com.example.myapplication D/native-armv8a: out dst: 400
参考资料
1.《ARMv8-A-Programmer-Guide》
2.《Arm® A64 Instruction Set Architecture Armv8, for Armv8-A architecture profile》