欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【NEON 和 VFP 编程】NEON加载、存储元素和结构指令

程序员文章站 2022-04-19 16:21:32
...

本节包括以下小节:

• 交叉存取。

• 加载/存储元素和结构指令中的对齐限制。

• VLDn 和 VSTn(单个 n 元素结构到一条向量线)。

此类指令几乎可用于所有数据访问。可加载标准向量 (n = 1)。

• VLDn(单个 n 元素结构到所有向量线)。

• VLDn 和 VSTn(多个 n 元素结构)。

一、交叉存取

此组中的许多指令在将结构存储到内存时提供交叉存取功能,并在从内存加载结构时提供反向交叉存取功能。下图显示了一个反向交叉存取示例。 交叉存取就是反向的过程。
【NEON 和 VFP 编程】NEON加载、存储元素和结构指令

二、加载/存储元素和结构指令中的对齐限制

其中许多指令允许指定内存对齐限制。 如果指令中未指定对齐,则由 A 位(CP15 寄存器 1 位 [1])控制对齐限制。

• 如果 A 位为 0,则没有对齐限制(但强序或设备内存除外,其访问必须对齐元素,否则会出现意外结果)。

• 如果 A 位为 1,则访问必须对齐元素。

如果地址未正确对齐,则会发生对齐故障。

三、VLDn 和 VSTn(单个 n 元素结构到一条向量线)

向量加载(单个 n 元素结构到一个向量线)将一个 n 元素结构从内存加载到一个或多个 NEON 寄存器。未加载的寄存器元素将保持不变。

语法

Vopn{cond}.datatype list, [Rn{@align}]{!}

Vopn{cond}.datatype list, [Rn{@align}], Rm

其中:

op 必须为 LD 或 ST。

n 必须为 1、2、3 或 4 之一。

cond 是一个可选的条件代码。

datatype 见下表。

list 指定 NEON 寄存器列表。 有关选项,见下表。

Rn 是包含基址的 ARM 寄存器。Rn 不得为 R15。

align 指定可选对齐。 有关选项,见下表。

! 如果 ! 存在,则将 Rn 更新为(Rn + 指令传送的字节数)。 在完成所有加载/存储后,执行该更新。

Rm 是一个包含基址偏移量的 ARM 寄存器。 如果 Rm 存在,则在使用该地址访问内存之后,将 Rn 更新为 (Rn + Rm)。Rm 不得为 R13 或 R15。
【NEON 和 VFP 编程】NEON加载、存储元素和结构指令

下面是单个 2 元素结构到一条向量线的示例。

    unsigned int *data = new unsigned int[4];
    unsigned int *data1 = new unsigned int[4];

    for (int i = 0; i < 4; i++) {
        data[i] = 0x0;
        data1[i] = (unsigned int) (i + 1);
        LOGI("1 data[%d]=%#0X data1[%d]=%#0X", i, data[i], i, data1[i]);
    }

    asm volatile(
            "VBIC q0,q0\t\n"
            "VLD2.32 {d0[0],d2[0]},[%1]\t\n"
            "VST2.32 {d0[0],d2[0]},[%0]\t\n"
    :"+r"(data),//%0
    "+r"(data1) //%1
    :
    : "memory", "q0", "q1"
    );

    for (int i = 0; i < 4; i++) {
        LOGI("2 data[%d]=%#0X data1[%d]=%#0X", i, data[i], i, data1[i]);
    }

运行结果:

11-11 08:38:28.860 3290-3290/ndk.example.com.ndkexample I/Native: 1 data[0]=0 data1[0]=0X1
    1 data[1]=0 data1[1]=0X2
    1 data[2]=0 data1[2]=0X3
    1 data[3]=0 data1[3]=0X4
    2 data[0]=0X1 data1[0]=0X1
    2 data[1]=0X2 data1[1]=0X2
    2 data[2]=0 data1[2]=0X3
    2 data[3]=0 data1[3]=0X4

先将data1中的前两个元素分别加载到D0[0]和D2[0]中,然后将他们存储到数组data中。

四、VLDn(单个 n 元素结构到所有向量线)

向量加载(单个 n 元素结构到所有向量线)将一个 n 元素结构的多个副本从内存加载到一个或多个 NEON 寄存器中。

语法

VLDn{cond}.datatype list, [Rn{@align}]{!}

VLDn{cond}.datatype list, [Rn{@align}], Rm

其中:

n 必须为 1、2、3 或 4 之一。

cond 是一个可选的条件代码。

datatype 见下表。

list 指定 NEON 寄存器列表。 有关选项,见下表。

Rn 是包含基址的 ARM 寄存器。Rn 不得为 R15。

align 指定可选对齐。 有关选项,见下表。

! 如果 ! 存在,则将 Rn 更新为(Rn + 指令传送的字节数)。 在完成所有加载/存储后,执行该更新。

Rm 是一个包含基址偏移量的 ARM 寄存器。 如果 Rm 存在,则在使用该地址访问内存之后,将 Rn 更新为 (Rn + Rm)。Rm 不得为 R13 或 R15。
【NEON 和 VFP 编程】NEON加载、存储元素和结构指令

将代码稍作修改即可。

    unsigned int *data = new unsigned int[4];
    unsigned int *data1 = new unsigned int[4];

    for (int i = 0; i < 4; i++) {
        data[i] = 0x0;
        data1[i] = (unsigned int) (i + 1);
        LOGI("1 data[%d]=%#0X data1[%d]=%#0X", i, data[i], i, data1[i]);
    }

    asm volatile(
            "VBIC q0,q0\t\n"
            "VBIC q1,q1\t\n"
            "VLD2.32 {d0[],d2[]},[%1]\t\n"
            "VSTM %0,{q0}\t\n"
            "VSTM %1,{q1}\t\n"
    :"+r"(data),//%0
    "+r"(data1) //%1
    :
    : "memory", "q0", "q1"
    );

    for (int i = 0; i < 4; i++) {
        LOGI("2 data[%d]=%#0X data1[%d]=%#0X", i, data[i], i, data1[i]);
    }

运行结果

11-11 09:08:06.200 3530-3530/ndk.example.com.ndkexample I/Native: 1 data[0]=0 data1[0]=0X1
    1 data[1]=0 data1[1]=0X2
    1 data[2]=0 data1[2]=0X3
    1 data[3]=0 data1[3]=0X4
    2 data[0]=0X1 data1[0]=0X2
    2 data[1]=0X1 data1[1]=0X2
    2 data[2]=0 data1[2]=0
    2 data[3]=0 data1[3]=0

说明d0寄存器中都是0x1,d2寄存器中都是0x2。

五、VLDn 和 VSTn(多个 n 元素结构)

向量加载(多个 n 元素结构)使用反向交叉存取功能,将多个 n 元素结构从内存加载到一个或多个 NEON 寄存器中(除非 n == 1)。 会加载每个寄存器的每个元素。

向量存储(多个 n 元素结构)使用交叉存取功能将多个 n 元素结构从一个或多个 NEON 寄存器存储到内存中(除非 n == 1)。 会存储每个寄存器的每个元素。

语法

Vopn{cond}.datatype list, [Rn{@align}]{!}

Vopn{cond}.datatype list, [Rn{@align}], Rm

其中:

op 必须为 LD 或 ST。

n 必须为 1、2、3 或 4 之一。

cond 是一个可选的条件代码。

datatype 有关选项,见下表。

list 指定 NEON 寄存器列表。 有关选项,见下表。

Rn 是包含基址的 ARM 寄存器。Rn 不得为 R15。

align 指定可选对齐。 有关选项,见下表。

! 如果 ! 存在,则将 Rn 更新为(Rn + 指令传送的字节数)。 在完成所有加载/存储后,执行该更新。

Rm 是一个包含基址偏移量的 ARM 寄存器。 如果 Rm 存在,则在使用该地址访问内存之后,将 Rn 更新为 (Rn + Rm)。Rm 不得为 R13 或 R15。
【NEON 和 VFP 编程】NEON加载、存储元素和结构指令

下面是代码示例片段,将上面的例子稍作修改。

    unsigned int *data = new unsigned int[4];
    unsigned int *data1 = new unsigned int[4];

    for (int i = 0; i < 4; i++) {
        data[i] = 0x0;
        data1[i] = (unsigned int) (i + 1);
        LOGI("1 data[%d]=%#0X data1[%d]=%#0X", i, data[i], i, data1[i]);
    }

    asm volatile(
            "VBIC q0,q0\t\n"
            "VBIC q1,q1\t\n"
            "VLD2.32 {d0,d2},[%1]\t\n"
            "VST2.32 {d0,d2},[%0]\t\n"
    :"+r"(data),//%0
    "+r"(data1) //%1
    :
    : "memory", "q0", "q1"
    );

    for (int i = 0; i < 4; i++) {
        LOGI("2 data[%d]=%#0X data1[%d]=%#0X", i, data[i], i, data1[i]);
    }

运行结果:

11-12 07:59:34.720 3084-3084/ndk.example.com.ndkexample I/Native: 1 data[0]=0 data1[0]=0X1
    1 data[1]=0 data1[1]=0X2
    1 data[2]=0 data1[2]=0X3
    1 data[3]=0 data1[3]=0X4
    2 data[0]=0X1 data1[0]=0X1
    2 data[1]=0X2 data1[1]=0X2
    2 data[2]=0X3 data1[2]=0X3
    2 data[3]=0X4 data1[3]=0X4