【ARMv8 SIMD和浮点指令编程】NEON 减法指令——减法也好几种

news/2024/2/29 3:16:36

向量减法包括常见的普通加指令,还包括长减、宽减、半减、饱和减、按对减、按对加并累加、选择高半部分结果加、全部元素加等。

1 SUB

减法(向量),该指令从第一个源 SIMD&FP 寄存器中的相应向量元素中减去第二个源 SIMD&FP 寄存器中的每个向量元素,将结果放入一个向量中,并将该向量写入目标 SIMD&FP 寄存器。

标量

在这里插入图片描述

SUB <V><d>, <V><n>, <V><m>

向量

在这里插入图片描述

SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

<V> 是宽度说明符,以“size”编码:

size<V>
0xRESERVED
10RESERVED
11D

<d> 是 SIMD&FP 目标寄存器的编号,在“Rd”字段中。

<n> 是第一个 SIMD&FP 源寄存器的编号,编码在“Rn”字段中。

<m> 是第二个 SIMD&FP 源寄存器的编号,编码在“Rm”字段中。

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<T> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
110RESERVED
1112D

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 SUB 指令的例子。

    auto *srcArr = new int[4];for (int i = 0; i < 4; i++) {srcArr[i] = 100 * (i + 1);}char *src = (char *) srcArr;LOGD("in srcArr: %d %d %d %d", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);asm volatile("LD1 {v0.4S}, [%[src]]\n""SUB v1.4S, v0.4S, v0.4S\n""ST1 {v1.4S}, [%[src]]\n":[src] "+r"(src):: "cc", "memory", "v0");LOGD("-----------------------------");LOGD("out srcArr: %d %d %d %d", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);delete[] srcArr;

SUB v1.4S, v0.4S, v0.4S 将 v0 寄存器 S 通道的值减去 v0 寄存器 S 通道的值的结果写入 v1 的 S 通道,不难知道最终结果均为 0。

运行结果:

2023-05-25 07:42:14.624 13572-13639/com.demo.myapplication D/NativeCore: in srcArr: 100 200 300 400
2023-05-25 07:42:14.624 13572-13639/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-25 07:42:14.624 13572-13639/com.demo.myapplication D/NativeCore: out srcArr: 0 0 0 0

2 SUBHN/SUBHN2

减法返回高窄,该指令从第一个源 SIMD&FP 寄存器中的相应向量元素中减去第二个源 SIMD&FP 寄存器中的每个向量元素,将结果的最高有效一半放入向量中,并将该向量写入目标 SIMD&FP 寄存器的下半部分或上半部分。 该指令中的所有值都是有符号整数值。

结果被截断。有关四舍五入的结果,请使用 RSUBHN。

SUBHN 指令将向量写入目标寄存器的下半部分并清除上半部分,而 SUBHN2 指令将向量写入目标寄存器的上半部分而不影响寄存器的其他位。

在这里插入图片描述

SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

下面是使用 SUBHN/SUBHN2 指令的例子。

    auto *srcArr = new unsigned long long int[4];auto *outArr = new unsigned int[4]{0};for (int i = 0; i < 4; i++) {srcArr[i] = 0x10010000000 * (i + 1);}char *src = (char *) srcArr;char *dst = (char *) outArr;LOGD("in srcArr: 0x%llx 0x%llx 0x%llx 0x%llx", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);LOGD("in outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);asm volatile("LD1 {v0.2D, v1.2D}, [%[src]]\n""SUBHN v2.2S, v1.2D, v0.2D\n""SUBHN2 v2.4S, v0.2D, v1.2D\n""ST1 {v2.4S}, [%[dst]]\n":[src] "+r"(src),[dst] "+r"(dst):: "cc", "memory", "v0", "v1", "v2");LOGD("-----------------------------");LOGD("out outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);delete[] srcArr;delete[] outArr;

SUBHN v2.2S, v1.2D, v0.2D 将 v1 的两个 D 通道的值减去 v0 的两个 D 通道的值,接着取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个低 S 通道,v2 寄存器高 S 通道全部清零。

SUBHN2 v2.4S, v0.2D, v1.2D 将 v0 的两个 D 通道的值减去 v1 的两个 D 通道的值,接着取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个高 S 通道,并保持 v2 其它位不变。

0x10010000000 - 0x30030000000 = 0x20020000000 - 0x40040000000 = 0xFFFF FDFF E000 0000,截断的方式取高位即 0xFFFF FDFF。

在这里插入图片描述

运行结果:

2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: in srcArr: 0x10010000000 0x20020000000 0x30030000000 0x40040000000
2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: in outArr: 0x0 0x0 0x0 0x0
2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-25 07:52:36.500 16479-16551/com.demo.myapplication D/NativeCore: out outArr: 0x200 0x200 0xfffffdff 0xfffffdff

3 USUBL/USUBL2

无符号长减,该指令从第一个源 SIMD&FP 寄存器的相应向量元素中减去第二个源 SIMD&FP 寄存器的下半部分或上半部分中的每个向量元素,将结果放入一个向量,并将该向量写入目标 SIMD&FP 寄存器。该指令中的所有值都是无符号整数值。目标向量元素的长度是源向量元素的两倍。

USUBL 指令从每个源寄存器的下半部分提取每个源向量。USUBL2 指令从每个源寄存器的上半部分提取每个源向量。

在这里插入图片描述

USUBL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 USUBL/USUBL2 指令的例子。

    auto *srcArr = new unsigned int[8];auto *outArr = new unsigned long long int[4]{0};for (int i = 0; i < 8; i++) {srcArr[i] = 100 * (i + 1);}char *src = (char *) srcArr;char *dst = (char *) outArr;LOGD("in srcArr: %u %u %u %u %u %u %u %u", srcArr[0], srcArr[1], srcArr[2], srcArr[3],srcArr[4], srcArr[5], srcArr[6], srcArr[7]);asm volatile("LD1 {v0.4S, v1.4S}, [%[src]]\n""USUBL v2.2D, v1.2S, v0.2S\n""USUBL2 v3.2D, v1.4S, v0.4S\n""ST1 {v2.2D, v3.2D}, [%[dst]]\n":[src] "+r"(src),[dst] "+r"(dst):: "cc", "memory", "v0", "v1", "v2", "v3");LOGD("-----------------------------");LOGD("out outArr: %llu %llu %llu %llu", outArr[0], outArr[1], outArr[2], outArr[3]);delete[] srcArr;delete[] outArr;

USUBL v2.2D, v1.2S, v0.2S 将 v1 寄存器低半部分两个 S 通道的值减去 v0 寄存器低半部分两个 S 通道的值,并将结果写入 v2 寄存器的两个 D 通道。

USUBL2 v3.2D, v1.4S, v0.4S 将 v1 寄存器高半部分两个 S 通道的值减去 v0 寄存器高半部分两个 S 通道的值,并将结果写入 v3 寄存器的两个 D 通道。

运行结果:

2023-05-26 07:56:51.966 28776-28866/com.demo.myapplication D/NativeCore: in srcArr: 100 200 300 400 500 600 700 800
2023-05-26 07:56:51.967 28776-28866/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-26 07:56:51.967 28776-28866/com.demo.myapplication D/NativeCore: out outArr: 400 400 400 400

4 USUBW/USUBW2

无符号宽减,该指令从第一个源 SIMD&FP 寄存器的下半部分或上半部分中的相应向量元素中减去第二个源 SIMD&FP 寄存器的每个向量元素,将结果放入一个向量中,并将该向量写入 SIMD&FP 目标寄存器。该指令中的所有值都是无符号整数值。目标寄存器和第一个源寄存器的向量元素是第二个源寄存器的向量元素的两倍长。

USUBW 指令从第一个源寄存器的下半部分提取向量元素。USUBW2 指令从第一个源寄存器的上半部分提取向量元素。

在这里插入图片描述

USUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

下面是使用 USUBW/USUBW2 指令的例子。

    auto *srcArr = new unsigned int[4];auto *outArr = new unsigned long long int[4]{0};for (int i = 0; i < 4; i++) {srcArr[i] = 100 * (i + 1);outArr[i] = 1000 * (i + 1);}char *src = (char *) srcArr;char *dst = (char *) outArr;LOGD("in srcArr: %u %u %u %u", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);LOGD("in outArr: %llu %llu %llu %llu", outArr[0], outArr[1], outArr[2], outArr[3]);asm volatile("LD1 {v0.4S}, [%[src]]\n""LD1 {v1.2D, v2.2D}, [%[dst]]\n""USUBW v3.2D, v1.2D, v0.2S\n""USUBW2 v4.2D, v2.2D, v0.4S\n""ST1 {v3.2D, v4.2D}, [%[dst]]\n":[src] "+r"(src),[dst] "+r"(dst):: "cc", "memory", "v0", "v1", "v2", "v3", "v4");LOGD("-----------------------------");LOGD("out outArr: %llu %llu %llu %llu", outArr[0], outArr[1], outArr[2], outArr[3]);delete[] srcArr;delete[] outArr;

USUBW v3.2D, v1.2D, v0.2S 将 v1 寄存器的两个 D 通道的值减去 v0 寄存器低半部分两个 S 通道的值 ,并将结果写入 v3 寄存器的两个 D 通道。

USUBW2 v4.2D, v2.2D, v0.4S 将 v2 寄存器的两个 D 通道的值减去 v0 寄存器高半部分两个 S 通道的值,并将结果写入 v4 寄存器的两个 D 通道。

在这里插入图片描述

运行结果:

2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: in srcArr: 100 200 300 400
2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: in outArr: 1000 2000 3000 4000
2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-26 08:06:59.172 31685-31752/com.demo.myapplication D/NativeCore: out outArr: 900 1800 2700 3600

5 UQSUB

无符号饱和减法,该指令从第一个源 SIMD&FP 寄存器的相应元素值中减去第二个源 SIMD&FP 寄存器的元素值,将结果放入向量中,并将向量写入目标 SIMD&FP 寄存器。如果任何结果发生溢出,这些结果就会饱和。如果发生饱和,则设置累积饱和位 FPSR.QC。

标量

在这里插入图片描述

UQSUB <V><d>, <V><n>, <V><m>

向量

在这里插入图片描述

UQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

<V> 是宽度说明符,以“size”编码:

size<V>
00B
01H
10S
11D

<d> 是 SIMD&FP 目标寄存器的编号,在“Rd”字段中。

<n> 是第一个 SIMD&FP 源寄存器的编号,编码在“Rn”字段中。

<m> 是第二个 SIMD&FP 源寄存器的编号,编码在“Rm”字段中。

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<T> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
110RESERVED
1112D

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 UQSUB 指令的例子。

    auto *srcArr = new unsigned int[4];auto *outArr = new unsigned int[4];long long fpsrBefore = 0, fpsrAfter = 0;for (int i = 0; i < 4; i++) {srcArr[i] = 0xFFFFFFFF;outArr[i] = i;}char *src = (char *) srcArr;char *dst = (char *) outArr;LOGD("in srcArr: %u %u %u %u", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);LOGD("in outArr: %u %u %u %u", outArr[0], outArr[1], outArr[2], outArr[3]);asm volatile("LD1 {v0.4S}, [%[src]]\n""LD1 {v1.4S}, [%[dst]]\n""MRS %[fpsrBefore], FPSR\n""UQSUB v2.4S, v1.4S, v0.4S\n""MRS %[fpsrAfter], FPSR\n""ST1 {v2.4S}, [%[dst]]\n":[src] "+r"(src),[dst] "+r"(dst),[fpsrBefore] "+r"(fpsrBefore),[fpsrAfter] "+r"(fpsrAfter):: "cc", "memory", "v0", "v1", "v2");LOGD("-----------------------------");LOGD("out outArr: %u %u %u %u", outArr[0], outArr[1], outArr[2], outArr[3]);LOGD("out fpsrBefore: 0x%llx fpsrAfter: 0x%llx", fpsrBefore, fpsrAfter);delete[] srcArr;delete[] outArr;

UQSUB v2.4S, v1.4S, v0.4S 将 v1 寄存器的 S 通道的值减去 v0 寄存器 S 通道的值,由于 v1 减去 v0 不够减,可以看到 FPSR.QC(fpsrAfter 内,即运行 UQSUB 指令后的 FPSR 寄存器值的副本)已经被置为 1(从低到高第 27 位(低位从 0 开始))。

运行结果:

2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: in srcArr: 4294967295 4294967295 4294967295 4294967295
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: in outArr: 0 1 2 3
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: -----------------------------
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: out outArr: 0 0 0 0
2023-05-26 08:45:39.794 17458-17538/com.demo.myapplication D/NativeCore: out fpsrBefore: 0x10 fpsrAfter: 0x8000010

6 UHSUB

无符号半减,该指令从第一个源 SIMD&FP 寄存器中的相应向量元素中减去第二个源 SIMD&FP 寄存器中的向量元素,将每个结果右移一位,将每个结果放入向量中,并将向量写入目标 SIMD&FP 寄存器。

在这里插入图片描述

UHSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<T> 是排列说明符,编码在“size:Q”字段中。它可以具有以下值:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

下面是使用 UHSUB 指令的例子。

    auto *srcArr = new unsigned int[4];auto *outArr = new unsigned int[4];for (int i = 0; i < 4; i++) {srcArr[i] = 0x01020304 * (i + 1);outArr[i] = 0x10203040 * (i + 1);}char *src = (char *) srcArr;char *dst = (char *) outArr;LOGD("in srcArr: 0x%x 0x%x 0x%x 0x%x", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);LOGD("in outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);asm volatile("LD1 {v0.4S}, [%[src]]\n""LD1 {v1.4S}, [%[dst]]\n""UHSUB v2.4S, v1.4S, v0.4S\n""ST1 {v2.4S}, [%[dst]]\n":[src] "+r"(src),[dst] "+r"(dst):: "cc", "memory", "v0", "v1", "v2");LOGD("-----------------------------");LOGD("out outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);delete[] srcArr;delete[] outArr;

我们计算一组数值:0x10203040 - 0x1020304 = 0xF1E2D3C = 0b1111 00011110 00101101 00111100,右移一位后即 0b0111 10001111 00010110 10011110 = 0x78F169E。

运行结果:

2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: in srcArr: 0x1020304 0x2040608 0x306090c 0x4080c10
2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: in outArr: 0x10203040 0x20406080 0x306090c0 0x4080c100
2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: -----------------------------
2023-05-27 17:45:40.672 26560-26560/com.example.myapplication D/native-armv8a: out outArr: 0x78f169e 0xf1e2d3c 0x16ad43da 0x1e3c5a78

7 RSUBHN/RSUBHN2

四舍五入减法返回高窄。该指令从第一个源 SIMD&FP 寄存器的相应向量元素中减去第二个源 SIMD&FP 寄存器的每个向量元素,将结果的最高有效一半放入向量,并将向量写入目标 SIMD&FP 寄存器的下半部分或上半部分。结果四舍五入。有关截断结果,请使用 SUBHN、SUBHN2。

RSUBHN 指令将向量写入目标寄存器的下半部分并清除上半部分,而 RSUBHN2 指令将向量写入目标寄存器的上半部分而不影响寄存器的其他位。

在这里插入图片描述

RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>

2 是第二个和上半部分说明符。如果存在,它会导致对保存较窄元素的寄存器的高 64 位执行操作,并以“Q”编码:

Q2
0[absent]
1[present]

<Vd> 是 SIMD&FP 目标寄存器的名称,在“Rd”字段中编码。

<Ta> 是排列说明符,以“size”编码:

size<Ta>
008H
014S
102D
11RESERVED

<Vn> 是第一个 SIMD&FP 源寄存器的名称,在“Rn”字段中编码。

<Vm> 是第二个 SIMD&FP 源寄存器的名称,在“Rm”字段中编码。

<Tb> 是排列说明符,编码为“size:Q”:

sizeQ<T>
0008B
00116B
0104H
0118H
1002S
1014S
11xRESERVED

下面是使用 RSUBHN/RSUBHN2 指令的例子。

    auto *srcArr = new unsigned long long int[4];auto *outArr = new unsigned int[4]{0};for (int i = 0; i < 4; i++) {srcArr[i] = 0x10010000000 * (i + 1);}char *src = (char *) srcArr;char *dst = (char *) outArr;LOGD("in srcArr: 0x%llx 0x%llx 0x%llx 0x%llx", srcArr[0], srcArr[1], srcArr[2], srcArr[3]);LOGD("in outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);asm volatile("LD1 {v0.2D, v1.2D}, [%[src]]\n""RSUBHN v2.2S, v1.2D, v0.2D\n""RSUBHN2 v2.4S, v0.2D, v1.2D\n""ST1 {v2.4S}, [%[dst]]\n":[src] "+r"(src),[dst] "+r"(dst):: "cc", "memory", "v0", "v1", "v2");LOGD("-----------------------------");LOGD("out outArr: 0x%x 0x%x 0x%x 0x%x", outArr[0], outArr[1], outArr[2], outArr[3]);delete[] srcArr;delete[] outArr;

RSUBHN v2.2S, v1.2D, v0.2D 将 v1 的两个 D 通道的值减去 v0 的两个 D 通道的值,接着四舍五入取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个低 S 通道,v2 寄存器高 S 通道全部清零。

RSUBHN2 v2.4S, v0.2D, v1.2D 将 v0 的两个 D 通道的值减去 v1 的两个 D 通道的值,接着四舍五入取结果的高位(也就是 D 通道的高 S 通道部分)写入 v2 的两个高 S 通道,并保持 v2 其它位不变。

0x10010000000 - 0x30030000000 = 0x20020000000 - 0x40040000000 = 0xFFFF FDFF E000 0000,四舍五入的方式取高位即 0xFFFF FE00。

运行结果:

2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: in srcArr: 0x10010000000 0x20020000000 0x30030000000 0x40040000000
2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: in outArr: 0x0 0x0 0x0 0x0
2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: -----------------------------
2023-05-28 07:25:14.779 21437-21437/com.example.myapplication D/native-armv8a: out outArr: 0x200 0x200 0xfffffe00 0xfffffe00

8 其他

SHSUB —— 有符号半减

SQSUB —— 有符号饱和减法

SSUBL/SSUBL2 —— 有符号长减

SSUBW/SSUBW2 —— 有符号宽减

参考资料

1.《ARMv8-A-Programmer-Guide》
2.《Arm® A64 Instruction Set Architecture Armv8, for Armv8-A architecture profile》


https://www.jiucaihua.cn/news/show-4628046.html

相关文章

Java 实现在顺序表指定位置插入一个元素

一、思路 1.定义一个pos变量来记录要插入的位置. 2.定义一个usedSize变量来记录元素个数. 3.定义一个data变量来记录要插入的元素值. 4.要保证pos位置合法&#xff0c;也就是不是负数&#xff0c;因为是要保证pos位置前是要有元素&#xff0c;因此也不能大于元素个数. 5.也需要…

PyTorch深度学习实战(2)——PyTorch基础

PyTorch深度学习实战&#xff08;2&#xff09;——PyTorch基础 0. 前言1. 搭建 PyTorch 环境2. PyTorch 张量2.1 张量初始化2.2 张量运算2.3 张量对象的自动梯度计算 3. PyTorch 张量相对于 NumPy 数组的优势小结系列链接 0. 前言 PyTorch 是广泛应用于机器学习领域中的强大开…

Rust每日一练(Leetday0020) 最后单词的长度、螺旋矩阵II、排列序列

目录 58. 最后一个单词的长度 Length of Last Word &#x1f31f; 59. 螺旋矩阵 II Spiral Matrix II &#x1f31f;&#x1f31f; 60. 排列序列 Permutation Sequence &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日…

Linux之理解文件系统——文件的管理

文章目录 前言一、磁盘1.磁盘的物理结构2.磁盘的存储结构3.磁盘的逻辑结构 二、文件系统与inode1.文件在磁盘中是如何存储的&#xff1f;2.对文件进行操作 三、软硬链接1.软链接创建软链接&#xff1a;inode删除软链接&#xff1a;软链接的作用&#xff1a; 2.硬链接创建硬链接…

JavaScript之BOM(八)

JavaScript之BOM 1、BOM中的对象2、window对象2.1、简介2.2、常用的属性与方法2.3、常用的事件2.4、定时器和延时器 3、navigator 常用属性与方法4、history 常用属性与方法5、location 常用属性与方法 BOM&#xff1a;浏览器对象模型&#xff08;Browser Object Model&#xf…

阿里云 Windows Server 2022 安装 Docker

阿里云Windows Server 2022 安装 Docker 文章目录 情景尝试正解 安装Docker管理工具安装Docker重启系统配置Docker系统路径配置Docker引擎(也许不用)启动Docker服务 情景 情景&#xff1a;最近一直在搞微服务&#xff0c;团队的服务器是阿里云的 Windows Server 2022&…

论文笔记--Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context

论文笔记--Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context 1. 文章简介2. 文章概括3 文章重点技术3.1 Segment-Level Recurrence with State Reuse3.2 相对位置编码 4. 文章亮点5. 原文传送门 1. 文章简介 标题&#xff1a;Transformer-XL: Attent…

Golang每日一练(leetDay0086) 回文链表、删除链表节点

目录 234. 回文链表 Palindrome Linked-list &#x1f31f; 237. 删除链表中的节点 Delete Node In a Linked-list &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练…

MMPose(openmmlab AI实战营二期第一节)

链接&#xff1a;人体关键点检测与MMPose_哔哩哔哩_bilibili 赶了个进度&#xff0c;实际上没听到&#xff0c;一个方向被浓缩成50分钟是有点难度。后续有需要再回顾吧 人体姿态估计&#xff1a;识别人体关键点坐标。模式识别任务&#xff0c;难点是始终在变化。以关键点连线…

软件外包开发的测试用例

软件测试用例是一组详细的步骤、输入数据、预期结果和实际结果&#xff0c;用于验证软件是否满足特定需求或功能。编写测试用例的目的是确保软件的质量和性能。今天和大家分享编写软件测试用例的一般步骤&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;…