【ARMv8 编程】A64 内存访问指令——内存加载指令

news2024/11/28 8:48:07

与所有先前的 ARM 处理器一样,ARMv8 架构是一种加载/存储架构。这意味着没有数据处理指令直接对内存中的数据进行操作。数据必须首先被加载到寄存器中,修改,然后存储到内存中。该程序必须指定地址、要传输的数据大小以及源或目标寄存器。有额外的加载和存储指令提供更多选项,例如非临时加载/存储、加载/存储独占和获取/释放。

Load 指令的一般形式如下:

LDR Rt, <addr>

对于加载到整数寄存器中的指令,可以选择要加载的大小。例如,要加载小于指定寄存器值的大小,请将以下后缀之一附加到 LDR 指令:

  • LDRB(8 位,零扩展)
  • LDRSB(8 位,符号扩展)
  • LDRH(16 位,零扩展)
  • LDRSH(16 位,符号扩展)
  • LDRSW(32 位,符号扩展)

下面是 LDRSB 和 LDRB 加载示例图:

在这里插入图片描述

还有 unscaled-offset 的偏移形式,例如 LDUR<type>。 程序员通常不需要显式使用 LDUR 形式,因为大多数汇编器可以根据使用的偏移量选择合适的版本。

我们不需要指定 X 寄存器的零扩展加载,因为写入 W 寄存器实际上是零扩展到整个寄存器宽度。

下面是在 Android 平台 Log 打印使用的代码,下面 Demo 代码中会使用到。

#define LOG_TAG "NativeCore"
#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

1 LDR(立即数)

LDR 指令加载寄存器(立即偏移量)。它有 3 种类型编码:后索引(Post-index)、前索引(Pre-index)和无符号偏移(Unsigned offset)。

Post-index

在这里插入图片描述

32-bit (size = 10)

LDR <Wt>, [<Xn|SP>], #<simm>

64-bit (size = 11)

LDR <Xt>, [<Xn|SP>], #<simm>

Pre-index

在这里插入图片描述

32-bit (size = 10)

LDR <Wt>, [<Xn|SP>, #<simm>]!

64-bit (size = 11)

LDR <Xt>, [<Xn|SP>, #<simm>]!

Unsigned offset

在这里插入图片描述

32-bit (size = 10)

LDR <Wt>, [<Xn|SP>{, #<pimm>}]

64-bit (size = 11)

LDR <Xt>, [<Xn|SP>{, #<pimm>}]

<Wt> 是要传输的通用寄存器的 32 位名称,编码在“Rt”字段中。

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<simm> 是带符号的立即字节偏移量,在 -256 到 255 的范围内,在“imm9”字段中编码。

<pimm> 是可选的正立即字节偏移量。对于 32 位变体:是 0 到 16380 范围内 4 的倍数,默认为 0 并在“imm12”字段中编码为 <pimm>/4。对于 64 位变体:是 0 到 32760 范围内 8 的倍数,默认为 0 并在“imm12”字段中编码为 <pimm>/8。

LDR 指令 Post-index

下面是使用 LDR 指令 Post-index 的例子。

  1. 新建 src_arr、dst_arr 数组,并初始化为 0。
  2. 给 src_arr 赋初值,将 long long int* 指针转为 char*,为了后续内联汇编加载(笔者环境实测:如果不这样赋值 STR 指令无法正常写入内存)。
  3. 经过内联汇编代码运行,MOV X3, %x[len] 先将数组长度 len 移动到 X3 寄存器,接着 1 是标签,LDR X2, [%x[src]], #8 将 src_arr 数组的前 8 个字节加载到 X2 寄存器,接着 Post-index(也就是地址加 8 写回,下次读取起点已经加 8)。STR X2, [%x[dst]], #8 会将刚刚 X2 寄存器的值写入 dst_arr 内存中,同样 Post-index(也就是地址加 8 写回,下次写入起点已经加 8)。SUBS X3, X3, #1 将 X3 每次都减去 1,SUBS 会设置条件标志,后面 B.GT 1b 判断当 SUBS 设置条件标志 Z = 1 时,就不满足跳转到标签 1 处执行的条件,也就退出了内联汇编代码,否则跳转到标签 1 处继续执行。
  4. 继续打印一次 dst_arr 数组,查看内部值,此时和 src_arr 内的值保持一致,说明 LDR 和 STR 都生效了。
    long long int len = 10;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0101010101010101 * i;
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDR X2, [%x[src]], #8\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

下面是运行结果,首先打印出 src_arr 和 dst_arr 的值,此处知道 dst_arr 已经清零。后面一次 dst_arr 打印,则是经过了内联汇编代码。

    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[0]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[1]=0x101010101010101
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[2]=0x202020202020202
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[3]=0x303030303030303
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[4]=0x404040404040404
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[5]=0x505050505050505
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[6]=0x606060606060606
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[7]=0x707070707070707
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[8]=0x808080808080808
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: src_arr[9]=0x909090909090909
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: =============================
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[3]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[4]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[5]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[6]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[7]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[8]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[9]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: -----------------------------
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[1]=0x101010101010101
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[2]=0x202020202020202
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[3]=0x303030303030303
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[4]=0x404040404040404
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[5]=0x505050505050505
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[6]=0x606060606060606
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[7]=0x707070707070707
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[8]=0x808080808080808
    2023-04-13 18:46:34.838 11692-11801/com.demo.myapplication D/NativeCore: dst_arr[9]=0x909090909090909

LDR 指令 Pre-index

下面是使用 LDR 指令 Pre-index 的例子,也就是 index 提前加 8 的意思。将程序稍做修改,因为 index 提前加 8,为了防止发生内存读取越界,因此引入 SUB X3, X3, #1 先减 1,其次 LDR 指令也换为了 Pre-index 的格式 LDR X2, [%x[src], #8]!,地址先加 8 再去读取内存中的值到寄存器 X2。

    long long int len = 10;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0101010101010101 * i;
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "SUB X3, X3, #1\n"
        "1:\n"
        "LDR X2, [%x[src], #8]!\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[0]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[1]=0x101010101010101
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[2]=0x202020202020202
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[3]=0x303030303030303
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[4]=0x404040404040404
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[5]=0x505050505050505
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[6]=0x606060606060606
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[7]=0x707070707070707
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[8]=0x808080808080808
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: src_arr[9]=0x909090909090909
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: =============================
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[3]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[4]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[5]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[6]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[7]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[8]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[9]=0x0
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: -----------------------------
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[0]=0x101010101010101
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[1]=0x202020202020202
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[2]=0x303030303030303
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[3]=0x404040404040404
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[4]=0x505050505050505
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[5]=0x606060606060606
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[6]=0x707070707070707
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[7]=0x808080808080808
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[8]=0x909090909090909
    2023-04-13 19:22:58.314 19791-19862/com.demo.myapplication D/NativeCore: dst_arr[9]=0x0

LDR 指令 Unsigned offset

下面是使用 LDR 指令 Unsigned offset 的例子,也就是每次从这个 offset(无符号数)读取。

    long long int len = 10;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0101010101010101 * i;
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDR X2, [%x[src], #8]\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

    2023-04-14 08:05:08.059 7157-7305/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[0]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[1]=0x101010101010101
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[2]=0x202020202020202
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[3]=0x303030303030303
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[4]=0x404040404040404
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[5]=0x505050505050505
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[6]=0x606060606060606
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[7]=0x707070707070707
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[8]=0x808080808080808
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: src_arr[9]=0x909090909090909
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: =============================
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[3]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[4]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[5]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[6]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[7]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[8]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[9]=0x0
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: -----------------------------
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[0]=0x101010101010101
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[1]=0x101010101010101
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[2]=0x101010101010101
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[3]=0x101010101010101
    2023-04-14 08:05:08.060 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[4]=0x101010101010101
    2023-04-14 08:05:08.061 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[5]=0x101010101010101
    2023-04-14 08:05:08.061 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[6]=0x101010101010101
    2023-04-14 08:05:08.061 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[7]=0x101010101010101
    2023-04-14 08:05:08.061 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[8]=0x101010101010101
    2023-04-14 08:05:08.061 7157-7305/com.demo.myapplication D/NativeCore: dst_arr[9]=0x101010101010101

2 LDR(literal)

LDR(文字)指令根据 PC 值和立即偏移量计算地址,从内存加载一个字,并将其写入寄存器。

在这里插入图片描述

32-bit (opc = 00)

LDR <Wt>, <label>

64-bit (opc = 01)

LDR <Xt>, <label>

<Wt> 是要加载的通用寄存器的 32 位名称,编码在“Rt”字段中。

<Xt> 是要加载的通用寄存器的 64 位名称,编码在“Rt”字段中。

<label> 是要从中加载数据的程序标签。它与该指令地址的偏移量在 +/-1MB 范围内,编码为“imm19”乘以 4。

下面是使用 LDR(literal) 指令的例子。

    long long int x = 0;

    asm volatile(
        "LDR X2, abc\n"
        "MOV %x[x], X2\n"
        "abc:\n"
        "MOV X2, #0\n"
        "MOV X2, #1\n"
        "MOV X2, #2\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 LDR X2, abc 将 abc 标签根据 PC 值和立即偏移量计算地址,将这个地址的内容加载到寄存器。此时 x 的值打印出来它固定为 0xd2800022d2800002,它是什么呢?实际就是 MOV X2, #0MOV X2, #1 这两条指令。每条指令都编码为 32 位。

温习一下 MOV (wide immediate)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-StgTzt7P-1682724688018)(https://note.youdao.com/yws/res/112016/WEBRESOURCE199789001157b11eae15547fb4c87e34)]

0xd2800022d2800002 的二进制为 0b1 10 100101 00 0000000000000001 00010 1 10 100101 00 0000000000000000 00010(实际上这些位都是连续的,为了方便解读指令编码做了一些空白),0b1 10 100101 00 0000000000000001 00010 正好是 MOV X2, #1 的指令编码,而 0b1 10 100101 00 0000000000000000 00010则是 MOV X2, #0 的指令编码。

再来用 IDA 工具反编译一下这段代码:

在这里插入图片描述

我们可以清晰看到 MOV X2, #2 这条指令,却看不到它上面的另外两条 MOV (wide immediate) 指令,因为它们编码在 0xd2800022d2800002 内了。 并且 LDR X2, abc 替换成了 LDR X2, =0xD2800022D2800002,这和我们的运行结果完全一致。

3 LDR(寄存器)

LDR(寄存器)指令根据基址寄存器值和偏移寄存器值计算地址,从内存中加载一个字,并将其写入寄存器。偏移寄存器值可以选择性地移位和扩展。

在这里插入图片描述

32-bit (size = 10)

LDR <Wt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]

64-bit (size = 11)

LDR <Xt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<Wm>option<0> 设置为 0 时,是通用索引寄存器的 32 位名称,编码在“Rm”字段中。

<Xm>option<0> 设置为 1 时,是通用索引寄存器的 64 位名称,编码在“Rm”字段中。

<extend> 是索引扩展/移位说明符,默认为 LSL,当省略 <amount> 时,LSL 选项必须省略。在“option”字段中编码。它可以具有以下值:

<extend>option 编码
UXTW010
LSL011
SXTW110
SXTX111

<amount> 是索引移位量。仅当 <extend> 不是 LSL 时才可选。在允许可选的地方,它默认为 #0。

对于 32 位变体:它在“S”字段中编码可以具有以下值:

<amount>S 编码
#00
#21

对于 64 位变体:它在“S”字段中编码可以具有以下值:

<amount>S 编码
#00
#31

下面是使用 LDR(寄存器)指令的例子。

    long long int len = 3;
    
    long long int x = 0;
    auto *y_arr = new long long int[len]{0};
    
    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        y_arr[i] = 0x0102030410203040 * (i + 1);
        LOGD("y_arr[%d]=0x%llx", i, y_arr[i]);
    }
    
    char* y = (char*)y_arr;

    asm volatile(
        "MOV X3, #1\n"
        "LDR %x[x], [%x[y] ,X3, LSL#3]\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");
    
    LOGD("-----------------------------");
    LOGD("x=0x%llx", x);
    
    delete[] y_arr;

运行结果如下:

    2023-04-15 21:14:22.007 20790-20790/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
    2023-04-15 21:14:22.007 20790-20790/com.example.myapplication D/native-armv8a: y_arr[0]=0x102030410203040
    2023-04-15 21:14:22.007 20790-20790/com.example.myapplication D/native-armv8a: y_arr[1]=0x204060820406080
    2023-04-15 21:14:22.007 20790-20790/com.example.myapplication D/native-armv8a: y_arr[2]=0x306090c306090c0
    2023-04-15 21:14:22.007 20790-20790/com.example.myapplication D/native-armv8a: -----------------------------
    2023-04-15 21:14:22.007 20790-20790/com.example.myapplication D/native-armv8a: x=0x204060820406080

执行 LDR %x[x], [%x[y] ,X3, LSL#3],首先经过 MOV X3, #1 指令后 X3 的值为 1,在 X3 索引寄存器上逻辑左移 3 位,也就是 1 * 8 = 8,最终在 y_arr(%x[y] 内装载的实际上就是 y_arr 数组的基址) 上准备加载的索引就是 8,也就将 8 ~ 15 这些索引指向的字节加载到 %x[x] 寄存器,所以最终 x 的值为 0x204060820406080(和 y_arr 中第二个值是相同的)。

4 LDRB(立即数)

加载寄存器字节(立即数)指令从内存中加载一个字节,对其进行零扩展,并将结果写入寄存器。用于加载的地址是根据基址寄存器和立即偏移量计算得出的。

Post-index

在这里插入图片描述

LDRB <Wt>, [<Xn|SP>], #<simm>

Pre-index

在这里插入图片描述

LDRB <Wt>, [<Xn|SP>, #<simm>]!

Unsigned offset

在这里插入图片描述

LDRB <Wt>, [<Xn|SP>{, #<pimm>}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<simm> 是带符号的立即字节偏移量,在 -256 到 255 的范围内,在“imm9”字段中编码。

<pimm> 是可选的正立即字节偏移量,范围为 0 到 4095,默认为 0 并在“imm12”字段中编码。

LDRB(立即数)Post-index

下面是使用 LDRB(立即数)Post-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203040 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRB W2, [%x[src]], #8\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

从结果不难知道 LDRB W2, [%x[src]], #8 确实只加载了一个字节到 W2 寄存器,并把寄存器高位全部清零,这里还能确定的一点是数据是按照小端在内存中分布的。运行结果如下:

    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: src_arr[0]=0x102030410203040
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: src_arr[1]=0x204060820406080
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: src_arr[2]=0x306090c306090c0
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: =============================
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: dst_arr[0]=0x0
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: dst_arr[1]=0x0
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: -----------------------------
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: dst_arr[0]=0x40
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: dst_arr[1]=0x80
    2023-04-16 15:26:19.658 14573-14573/com.example.myapplication D/native-armv8a: dst_arr[2]=0xc0

LDRB(立即数)Pre-index

下面是使用 LDRB(立即数)Pre-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203040 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "SUB X3, X3, #1\n"
        "1:\n"
        "LDRB W2, [%x[src], #8]!\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: src_arr[0]=0x102030410203040
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: src_arr[1]=0x204060820406080
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: src_arr[2]=0x306090c306090c0
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: =============================
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: dst_arr[0]=0x0
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: dst_arr[1]=0x0
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: -----------------------------
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: dst_arr[0]=0x80
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: dst_arr[1]=0xc0
    2023-04-16 15:37:29.915 19872-19872/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0

LDRB(立即数)Unsigned offset

下面是使用 LDRB(立即数)Unsigned offset 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203040 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRB W2, [%x[src], #6]\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: src_arr[0]=0x102030410203040
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: src_arr[1]=0x204060820406080
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: src_arr[2]=0x306090c306090c0
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: =============================
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: dst_arr[0]=0x0
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: dst_arr[1]=0x0
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: -----------------------------
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: dst_arr[0]=0x2
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: dst_arr[1]=0x2
    2023-04-16 15:43:05.327 24289-24289/com.example.myapplication D/native-armv8a: dst_arr[2]=0x2

5 LDRB(寄存器)

加载寄存器字节(寄存器)指令根据基址寄存器值和偏移寄存器值计算地址,从内存中加载一个字节,对其进行零扩展,并将其写入寄存器。

在这里插入图片描述

Extended register (option != 011)

LDRB <Wt>, [<Xn|SP>, (<Wm>|<Xm>), <extend> {<amount>}]

Shifted register (option = 011)

LDRB <Wt>, [<Xn|SP>, <Xm>{, LSL <amount>}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<Wm>option<0> 设置为 0 时,是通用索引寄存器的 32 位名称,编码在“Rm”字段中。

<Xm>option<0> 设置为 1 时,是通用索引寄存器的 64 位名称,编码在“Rm”字段中。

<extend> 是索引扩展说明符,在“option”字段中编码。它可以具有以下值:

<extend>option 编码
UXTW010
SXTW110
SXTX111

<amount> 是索引移位量,必须是#0,如果省略则在“S”中编码为0,如果存在则编码为1。

下面是使用 LDRB(寄存器)指令的例子。

    long long int len = 3;

    long int x = 0;
    auto *y_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        y_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("y_arr[%d]=0x%llx", i, y_arr[i]);
    }

    char* y = (char*)y_arr;

    asm volatile(
    "MOV X3, #16\n"
    "LDRB %w[x], [%x[y] ,X3, LSL#0]\n"
    :[x] "+r"(x),
    [y] "+r"(y)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    LOGD("x=0x%lx", x);

    delete[] y_arr;

运行结果如下:

    2023-04-16 16:07:58.615 20132-20132/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
    2023-04-16 16:07:58.615 20132-20132/com.example.myapplication D/native-armv8a: y_arr[0]=0x102030410203050
    2023-04-16 16:07:58.615 20132-20132/com.example.myapplication D/native-armv8a: y_arr[1]=0x2040608204060a0
    2023-04-16 16:07:58.615 20132-20132/com.example.myapplication D/native-armv8a: y_arr[2]=0x306090c306090f0
    2023-04-16 16:07:58.615 20132-20132/com.example.myapplication D/native-armv8a: -----------------------------
    2023-04-16 16:07:58.615 20132-20132/com.example.myapplication D/native-armv8a: x=0xf0

MOV X3, #16 将索引立即数 16 移动到 X3 寄存器,LDRB %w[x], [%x[y] ,X3, LSL#0] 则首先对索引逻辑左移 0 位,也就是没变化还是 16,接着就从 16 这个位置加载 y_arr 中的字节,也就是 0xF0,写入 %w[x],并把寄存器高位清零,所以最终 x 的值就为 0xF0。

6 LDRSB(立即数)

加载寄存器有符号字节(立即数)指令从内存中加载一个字节,将其符号扩展为 32 位或 64 位,并将结果写入寄存器。用于加载的地址是根据基址寄存器和立即偏移量计算得出的。

Post-index

在这里插入图片描述

32-bit (opc = 11)

LDRSB <Wt>, [<Xn|SP>], #<simm>

64-bit (opc = 10)

LDRSB <Xt>, [<Xn|SP>], #<simm>

Pre-index

在这里插入图片描述

32-bit (opc = 11)

LDRSB <Wt>, [<Xn|SP>, #<simm>]!

64-bit (opc = 10)

LDRSB <Xt>, [<Xn|SP>, #<simm>]!

Unsigned offset

在这里插入图片描述

32-bit (opc = 11)

LDRSB <Wt>, [<Xn|SP>{, #<pimm>}]

64-bit (opc = 10)

LDRSB <Xt>, [<Xn|SP>{, #<pimm>}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<simm> 是带符号的立即字节偏移量,在 -256 到 255 的范围内,在“imm9”字段中编码。

<pimm> 是可选的正立即字节偏移量,范围为 0 到 4095,默认为 0 并在“imm12”字段中编码。

LDRSB(立即数)Post-index

下面是使用 LDRSB(立即数)Post-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRSB X2, [%x[src]], #8\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

我们看到 dst_arr 的最后两个值变为了负数,这就是 LDRSB 补符号位的作用。运行结果如下:

    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: src_arr[0]=0x102030410203050
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: src_arr[1]=0x2040608204060a0
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: src_arr[2]=0x306090c306090f0
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: =============================
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: dst_arr[0]=0x0
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: dst_arr[1]=0x0
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: -----------------------------
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: dst_arr[0]=0x50
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: dst_arr[1]=0xffffffffffffffa0
    2023-04-16 20:39:09.486 7564-7564/com.example.myapplication D/native-armv8a: dst_arr[2]=0xfffffffffffffff0

LDRSB(立即数)Pre-index

下面是使用 LDRSB(立即数)Pre-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "SUB X3, X3, #1\n"
        "1:\n"
        "LDRSB X2, [%x[src], #8]!\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

和 LDRSB(立即数)Post-index 指令的区别就是 index 前置自增了。运行结果如下:

2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: src_arr[0]=0x102030410203050
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: src_arr[1]=0x2040608204060a0
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: src_arr[2]=0x306090c306090f0
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: =============================
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: dst_arr[0]=0x0
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: dst_arr[1]=0x0
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: -----------------------------
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: dst_arr[0]=0xffffffffffffffa0
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: dst_arr[1]=0xfffffffffffffff0
2023-04-16 20:44:15.080 10777-10777/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0

LDRSB(立即数)Unsigned offset

下面是使用 LDRSB(立即数)Unsigned offset 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRSB X2, [%x[src], #6]\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

除了加载固定位置的字节外,由于字节的符号位为 0,所以没有出现负数的行为!运行结果如下:

    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: +++++++++++++++++++++++++++++
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: src_arr[0]=0x102030410203050
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: src_arr[1]=0x2040608204060a0
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: src_arr[2]=0x306090c306090f0
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: =============================
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: dst_arr[0]=0x0
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: dst_arr[1]=0x0
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: dst_arr[2]=0x0
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: -----------------------------
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: dst_arr[0]=0x2
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: dst_arr[1]=0x2
    2023-04-16 20:48:49.703 13214-13214/com.example.myapplication D/native-armv8a: dst_arr[2]=0x2

7 LDRSB(寄存器)

加载寄存器有符号字(寄存器)指令根据基址寄存器值和偏移寄存器值计算地址,从内存中加载一个字节,对其进行符号扩展,并将其写入寄存器。

在这里插入图片描述

32-bit extended register offset (opc = 11 && option != 011)

LDRSB <Wt>, [<Xn|SP>, (<Wm>|<Xm>), <extend> {<amount>}]

32-bit shifted register offset (opc = 11 && option = 011)

LDRSB <Wt>, [<Xn|SP>, <Xm>{, LSL <amount>}]

64-bit extended register offset (opc = 10 && option != 011)

LDRSB <Xt>, [<Xn|SP>, (<Wm>|<Xm>), <extend> {<amount>}]

64-bit shifted register offset (opc = 10 && option = 011)

LDRSB <Xt>, [<Xn|SP>, <Xm>{, LSL <amount>}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<Wm>option<0> 设置为 0 时,是通用索引寄存器的 32 位名称,编码在“Rm”字段中。

<Xm>option<0> 设置为 1 时,是通用索引寄存器的 64 位名称,编码在“Rm”字段中。

<extend> 是索引扩展说明符,编码在“option”中:

option<extend>
010UXTW
110SXTW
111SXTX

<amount> 是索引移位量,必须是#0,如果省略则在“S”中编码为0,如果存在则编码为1。

下面是使用 LDRSB(寄存器)指令的例子。

    long long int len = 3;

    long int x = 0;
    auto *y_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        y_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("y_arr[%d]=0x%llx", i, y_arr[i]);
    }

    char* y = (char*)y_arr;

    asm volatile(
    "MOV X3, #16\n"
    "LDRSB %w[x], [%x[y] ,X3, LSL#0]\n"
    :[x] "+r"(x),
    [y] "+r"(y)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    LOGD("x=0x%lx", x);

    delete[] y_arr;

我们看到和 LDRB(寄存器)指令的区别是对符号位的处理。运行结果如下:

2023-04-17 08:13:44.105 12670-12733/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-17 08:13:44.105 12670-12733/com.demo.myapplication D/NativeCore: y_arr[0]=0x102030410203050
2023-04-17 08:13:44.105 12670-12733/com.demo.myapplication D/NativeCore: y_arr[1]=0x2040608204060a0
2023-04-17 08:13:44.105 12670-12733/com.demo.myapplication D/NativeCore: y_arr[2]=0x306090c306090f0
2023-04-17 08:13:44.105 12670-12733/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-17 08:13:44.105 12670-12733/com.demo.myapplication D/NativeCore: x=0xfffffff0

8 LDRH(立即数)

加载寄存器半字(立即数)指令从内存中加载一个半字,对其进行零扩展,并将结果写入寄存器。用于加载的地址是根据基址寄存器和立即偏移量计算得出的。

Post-index

在这里插入图片描述

LDRH <Wt>, [<Xn|SP>], #<simm>

Pre-index

在这里插入图片描述

LDRH <Wt>, [<Xn|SP>, #<simm>]!

Unsigned offset

在这里插入图片描述

LDRH <Wt>, [<Xn|SP>{, #<pimm>}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<simm> 是带符号的立即字节偏移量,在 -256 到 255 的范围内,在“imm9”字段中编码。

<pimm> 是可选的正立即字节偏移量,是 0 到 8190 范围内 2 的倍数,默认为 0 并在“imm12”字段中编码为 <pimm>/2

LDRH(立即数)Post-index

下面是使用 LDRH(立即数)Post-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203040 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRH W2, [%x[src]], #8\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

从结果不难知道 LDRH W2, [%x[src]], #8 确实只加载了两个字节(即半字)到 W2 寄存器,并把寄存器高位全部清零。运行结果如下:

2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: src_arr[0]=0x102030410203040
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: src_arr[1]=0x204060820406080
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: src_arr[2]=0x306090c306090c0
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: =============================
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: dst_arr[0]=0x3040
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: dst_arr[1]=0x6080
2023-04-18 07:47:47.383 15674-15741/com.demo.myapplication D/NativeCore: dst_arr[2]=0x90c0

LDRH(立即数)Pre-index

下面是使用 LDRH(立即数)Pre-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203040 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "SUB X3, X3, #1\n"
        "1:\n"
        "LDRH W2, [%x[src], #8]!\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

2023-04-18 07:51:27.771 17090-17178/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 07:51:27.771 17090-17178/com.demo.myapplication D/NativeCore: src_arr[0]=0x102030410203040
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: src_arr[1]=0x204060820406080
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: src_arr[2]=0x306090c306090c0
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: =============================
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: dst_arr[0]=0x6080
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: dst_arr[1]=0x90c0
2023-04-18 07:51:27.772 17090-17178/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0

LDRH(立即数)Unsigned offset

下面是使用 LDRH(立即数)Unsigned offset 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203040 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRH W2, [%x[src], #6]\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: src_arr[0]=0x102030410203040
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: src_arr[1]=0x204060820406080
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: src_arr[2]=0x306090c306090c0
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: =============================
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: dst_arr[0]=0x102
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: dst_arr[1]=0x102
2023-04-18 07:53:55.745 18154-18224/com.demo.myapplication D/NativeCore: dst_arr[2]=0x102

9 LDRH(寄存器)

加载寄存器半字(寄存器)指令根据基址寄存器值和偏移寄存器值计算地址,从内存中加载一个半字,对其进行零扩展,并将其写入寄存器。

在这里插入图片描述

LDRH <Wt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<Wm>option<0> 设置为 0 时,是通用索引寄存器的 32 位名称,编码在“Rm”字段中。

<Xm>option<0> 设置为 1 时,是通用索引寄存器的 64 位名称,编码在“Rm”字段中。

<extend> 是索引扩展/移位说明符,默认为 LSL,当省略 <amount> 时,LSL 选项必须省略。 在“option”中编码:

option<extend>
010UXTW
011LSL
110SXTW
111SXTX

<amount> 索引移位量,仅当<extend>不是 LSL 时可选。在允许可选的地方,它默认为#0。 它以“S”编码:

S<amount>
0#0
1#1

下面是使用 LDRH(寄存器)指令的例子。

    long long int len = 3;

    long int x = 0;
    auto *y_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        y_arr[i] = 0x0102030410205030 * (i + 1);
        LOGD("y_arr[%d]=0x%llx", i, y_arr[i]);
    }

    char* y = (char*)y_arr;

    asm volatile(
    "MOV X3, #16\n"
    "LDRH %w[x], [%x[y] ,X3, LSL#0]\n"
    :[x] "+r"(x),
    [y] "+r"(y)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    LOGD("x=0x%lx", x);

    delete[] y_arr;

注意看 LDRH(寄存器)指令的对符号位的处理方式,并没有扩展符号位。运行结果如下:

2023-04-18 08:03:02.690 19907-20036/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 08:03:02.690 19907-20036/com.demo.myapplication D/NativeCore: y_arr[0]=0x102030410205030
2023-04-18 08:03:02.690 19907-20036/com.demo.myapplication D/NativeCore: y_arr[1]=0x20406082040a060
2023-04-18 08:03:02.690 19907-20036/com.demo.myapplication D/NativeCore: y_arr[2]=0x306090c3060f090
2023-04-18 08:03:02.691 19907-20036/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 08:03:02.691 19907-20036/com.demo.myapplication D/NativeCore: x=0xf090

10 LDRSH(立即数)

加载寄存器带符号半字(立即数)指令从内存中加载半字,将其符号扩展为 32 位或 64 位,并将结果写入寄存器。用于加载的地址是根据基址寄存器和立即偏移量计算得出的。

Post-index

在这里插入图片描述

32-bit (opc == 11)

LDRSH <Wt>, [<Xn|SP>], #<simm>

64-bit (opc == 10)

LDRSH <Xt>, [<Xn|SP>], #<simm>

Pre-index

在这里插入图片描述

32-bit (opc == 11)

LDRSH <Wt>, [<Xn|SP>, #<simm>]!

64-bit (opc == 10)

LDRSH <Xt>, [<Xn|SP>, #<simm>]!

Unsigned offset

在这里插入图片描述

32-bit (opc == 11)

LDRSH <Wt>, [<Xn|SP>{, #<pimm>}]

64-bit (opc == 10)

LDRSH <Xt>, [<Xn|SP>{, #<pimm>}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<simm> 是带符号的立即字节偏移量,在 -256 到 255 的范围内,在“imm9”字段中编码。

<pimm> 是可选的正立即字节偏移量,是 0 到 8190 范围内 2 的倍数,默认为 0 并在“imm12”字段中编码为 <pimm>/2

LDRSH(立即数)Post-index

下面是使用 LDRSH(立即数)Post-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRSH X2, [%x[src]], #8\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

我们看到 dst_arr 的最后一个值变为了负数,这就是 LDRSH 补符号位的作用。运行结果如下:

2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: src_arr[0]=0x102030410203050
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: src_arr[1]=0x2040608204060a0
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: src_arr[2]=0x306090c306090f0
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: =============================
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: dst_arr[0]=0x3050
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: dst_arr[1]=0x60a0
2023-04-18 08:20:35.778 24768-24851/com.demo.myapplication D/NativeCore: dst_arr[2]=0xffffffffffff90f0

LDRSH(立即数)Pre-index

下面是使用 LDRSH(立即数)Pre-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "SUB X3, X3, #1\n"
        "1:\n"
        "LDRSH X2, [%x[src], #8]!\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

和 LDRSH(立即数)Post-index 指令的区别就是 index 前置自增了。运行结果如下:

2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: src_arr[0]=0x102030410203050
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: src_arr[1]=0x2040608204060a0
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: src_arr[2]=0x306090c306090f0
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: =============================
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: dst_arr[0]=0x60a0
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: dst_arr[1]=0xffffffffffff90f0
2023-04-18 08:22:29.119 25484-25614/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0

LDRSH(立即数)Unsigned offset

下面是使用 LDRSH(立即数)Unsigned offset 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x8102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRSH X2, [%x[src], #6]\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

除了加载固定位置的半字外,由于半字的符号位为 1,所以进行了补 1 操作(符号位扩展),运行结果如下:

2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: src_arr[0]=0x8102030410203050
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: src_arr[1]=0x2040608204060a0
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: src_arr[2]=0x8306090c306090f0
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: =============================
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: dst_arr[0]=0xffffffffffff8102
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: dst_arr[1]=0xffffffffffff8102
2023-04-18 08:25:22.617 27152-27230/com.demo.myapplication D/NativeCore: dst_arr[2]=0xffffffffffff8102

11 LDRSH(寄存器)

加载寄存器带符号半字(寄存器)指令根据基址寄存器值和偏移寄存器值计算地址,从内存加载半字,对其进行符号扩展,并将其写入寄存器。

在这里插入图片描述

32-bit (opc == 11)

LDRSH <Wt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]

64-bit (opc == 10)

LDRSH <Xt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]

<Wt> 是要传输的通用寄存器的 32 位名称,在“Rt”字段中编码。

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<Wm>option<0> 设置为 0 时,是通用索引寄存器的 32 位名称,编码在“Rm”字段中。

<Xm>option<0> 设置为 1 时,是通用索引寄存器的 64 位名称,编码在“Rm”字段中。

<extend> 是索引扩展/移位说明符,默认为 LSL,当省略 <amount> 时,LSL 选项必须省略。 在“option”中编码:

option<extend>
010UXTW
011LSL
110SXTW
111SXTX

<amount> 索引移位量,仅当<extend>不是 LSL 时可选。在允许可选的地方,它默认为#0。 它以“S”编码:

S<amount>
0#0
1#1

下面是使用 LDRSH(寄存器)指令的例子。

    long long int len = 3;

    long int x = 0;
    auto *y_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        y_arr[i] = 0x0102030410205030 * (i + 1);
        LOGD("y_arr[%d]=0x%llx", i, y_arr[i]);
    }

    char* y = (char*)y_arr;

    asm volatile(
    "MOV X3, #16\n"
    "LDRSH %w[x], [%x[y] ,X3, LSL#0]\n"
    :[x] "+r"(x),
    [y] "+r"(y)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    LOGD("x=0x%lx", x);

    delete[] y_arr;

注意看 LDRH(寄存器)指令的对符号位的处理方式,此处扩展了符号位。运行结果如下:

2023-04-18 08:07:02.317 21812-21904/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-18 08:07:02.317 21812-21904/com.demo.myapplication D/NativeCore: y_arr[0]=0x102030410205030
2023-04-18 08:07:02.317 21812-21904/com.demo.myapplication D/NativeCore: y_arr[1]=0x20406082040a060
2023-04-18 08:07:02.317 21812-21904/com.demo.myapplication D/NativeCore: y_arr[2]=0x306090c3060f090
2023-04-18 08:07:02.317 21812-21904/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-18 08:07:02.317 21812-21904/com.demo.myapplication D/NativeCore: x=0xfffff090

12 LDRSW(立即数)

加载寄存器有符号字(立即数)指令从内存中加载一个字,将其符号扩展为 64 位,并将结果写入寄存器。用于加载的地址是根据基址寄存器和立即偏移量计算得出的。

Post-index

在这里插入图片描述

LDRSW <Xt>, [<Xn|SP>], #<simm>

Pre-index

在这里插入图片描述

LDRSW <Xt>, [<Xn|SP>, #<simm>]!

Unsigned offset

在这里插入图片描述

LDRSW <Xt>, [<Xn|SP>{, #<pimm>}]

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<simm> 是带符号的立即字节偏移量,在 -256 到 255 的范围内,在“imm9”字段中编码。

<pimm> 是可选的正立即字节偏移量,是 0 到 16380 范围内的 4 的倍数,默认为 0 并在“imm12”字段中编码为 <pimm>/4

LDRSW(立即数)Post-index

下面是使用 LDRSW(立即数)Post-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRSW X2, [%x[src]], #8\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: src_arr[0]=0x102030410203050
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: src_arr[1]=0x2040608204060a0
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: src_arr[2]=0x306090c306090f0
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: =============================
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: dst_arr[0]=0x10203050
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: dst_arr[1]=0x204060a0
2023-04-19 07:54:08.461 3256-3487/com.demo.myapplication D/NativeCore: dst_arr[2]=0x306090f0

LDRSW(立即数)Pre-index

下面是使用 LDRSW(立即数)Pre-index 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x0102030450203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "SUB X3, X3, #1\n"
        "1:\n"
        "LDRSW X2, [%x[src], #8]!\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: src_arr[0]=0x102030450203050
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: src_arr[1]=0x2040608a04060a0
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: src_arr[2]=0x306090cf06090f0
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: =============================
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: dst_arr[0]=0xffffffffa04060a0
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: dst_arr[1]=0xfffffffff06090f0
2023-04-19 07:55:54.196 5861-5962/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0

LDRSW(立即数)Unsigned offset

下面是使用 LDRSW(立即数)Unsigned offset 指令的例子。

    long long int len = 3;

    auto *src_arr = new long long int[len]{0};
    auto *dst_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        src_arr[i] = 0x8102030410203050 * (i + 1);
        LOGD("src_arr[%d]=0x%llx", i, src_arr[i]);
    }

    LOGD("=============================");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    char *src = (char *) src_arr;
    char *dst = (char *) dst_arr;

    asm volatile(
        "MOV X3, %x[len]\n"
        "1:\n"
        "LDRSW X2, [%x[src], #6]\n"
        "STR X2, [%x[dst]], #8\n"
        "SUBS X3, X3, #1\n"
        "B.GT 1b\n"
    :[len] "+r"(len),
     [src] "+r"(src),
     [dst] "+r"(dst)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    for(int i = 0; i < len; i++){
        LOGD("dst_arr[%d]=0x%llx", i, dst_arr[i]);
    }

    delete[] src_arr;
    delete[] dst_arr;

运行结果如下:

2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: src_arr[0]=0x8102030410203050
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: src_arr[1]=0x2040608204060a0
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: src_arr[2]=0x8306090c306090f0
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: =============================
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: dst_arr[0]=0x0
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: dst_arr[1]=0x0
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: dst_arr[2]=0x0
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: dst_arr[0]=0x60a08102
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: dst_arr[1]=0x60a08102
2023-04-19 07:58:02.543 7440-7531/com.demo.myapplication D/NativeCore: dst_arr[2]=0x60a08102

13 LDRSW(literal)

加载寄存器有符号字(文字)指令根据 PC 值和立即偏移量计算地址,从内存加载一个字,并将其写入寄存器。

在这里插入图片描述

LDRSW <Xt>, <label>

<Xt> 是要加载的通用寄存器的 64 位名称,在“Rt”字段中编码。

<label> 是要从中加载数据的程序标签。它与该指令地址的偏移量在 +/-1MB 范围内,编码为“imm19”乘以 4。

下面是使用 LDRSW(literal) 指令的例子。

    long long int x = 0;

    asm volatile(
        "LDRSW X2, abc\n"
        "MOV %x[x], X2\n"
        "abc:\n"
        "MOV X2, #0\n"
        "MOV X2, #1\n"
        "MOV X2, #2\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 LDRSW X2, abc 将 abc 标签根据 PC 值和立即偏移量计算地址,将这个地址的内容加载到寄存器。此时 x 的值打印出来它固定为 0xffffffffd2800002,高半部分的字全部被填充为了 1(这是因为 0xd2800002 符号位扩展了),0xd2800002 实际就是 MOV X2, #0 这条指令,所以这个地址的内容仅包含一条指令,而 LDR(literal) 包含了两条指令。使用 IDA 反汇编工具可看到 MOV X2, #1MOV X2, #2 的确没有编码到这个地址指向的内容中。

14 LDRSW(寄存器)

加载寄存器有符号字(寄存器)指令根据基址寄存器值和偏移寄存器值计算地址,从内存中加载一个字,对其进行符号扩展以形成 64 位值,并将其写入寄存器。偏移寄存器值可以左移 0 或 2 位。

在这里插入图片描述

LDRSW <Xt>, [<Xn|SP>, (<Wm>|<Xm>){, <extend> {<amount>}}]

<Xt> 是要传输的通用寄存器的 64 位名称,在“Rt”字段中编码。

<Xn|SP> 是通用基址寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<Wm>option<0> 设置为 0 时,是通用索引寄存器的 32 位名称,编码在“Rm”字段中。

<Xm>option<0> 设置为 1 时,是通用索引寄存器的 64 位名称,编码在“Rm”字段中。

<extend> 是索引扩展/移位说明符,默认为 LSL,当省略 <amount> 时,LSL 选项必须省略。在“option”中编码:

option<extend>
010UXTW
011LSL
110SXTW
111SXTX

<amount> 索引移位量,仅当 <extend> 不是 LSL 时可选。在允许可选的地方,它默认为#0。 它以“S”编码:

S<amount>
0#0
1#2

下面是使用 LDRSW(寄存器)指令的例子。

    long long int len = 3;

    long long int x = 0;
    auto *y_arr = new long long int[len]{0};

    LOGD("+++++++++++++++++++++++++++++");
    for(int i = 0; i < len; i++){
        y_arr[i] = 0x0102030450205030 * (i + 1);
        LOGD("y_arr[%d]=0x%llx", i, y_arr[i]);
    }

    char* y = (char*)y_arr;

    asm volatile(
    "MOV X3, #4\n"
    "LDRSW %x[x], [%x[y] ,X3, LSL#2]\n"
    :[x] "+r"(x),
    [y] "+r"(y)
    :
    : "cc", "memory");

    LOGD("-----------------------------");
    LOGD("x=0x%llx", x);

    delete[] y_arr;

LDRSW %x[x], [%x[y] ,X3, LSL#2] 首先将索引寄存器的值逻辑左移 2 位,也就是将 X3 寄存器的值乘以 4,即索引最终的值为 4 * 4 = 16,接着使用索引的值去加载数组中的一个字,也就是 0xF060F090,扩展符号位后就变为了 0xFFFFFFFFF060F090,这也是最终 x 的值。运行结果如下:

2023-04-19 08:34:42.228 27863-27920/com.demo.myapplication D/NativeCore: +++++++++++++++++++++++++++++
2023-04-19 08:34:42.228 27863-27920/com.demo.myapplication D/NativeCore: y_arr[0]=0x102030450205030
2023-04-19 08:34:42.228 27863-27920/com.demo.myapplication D/NativeCore: y_arr[1]=0x2040608a040a060
2023-04-19 08:34:42.228 27863-27920/com.demo.myapplication D/NativeCore: y_arr[2]=0x306090cf060f090
2023-04-19 08:34:42.228 27863-27920/com.demo.myapplication D/NativeCore: -----------------------------
2023-04-19 08:34:42.228 27863-27920/com.demo.myapplication D/NativeCore: x=0xfffffffff060f090

参考资料

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/474533.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《使用深度神经网络对光电容积脉搏图进行归一化,以进行个体和群体比较》阅读笔记

目录 一、论文摘要 二、论文十问 Q1&#xff1a;论文试图解决什么问题&#xff1f; Q2&#xff1a;这是否是一个新的问题&#xff1f; Q3&#xff1a;这篇文章要验证一个什么科学假设&#xff1f; Q4&#xff1a;有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一…

symfonos 1(smtp注入webshell,配合文件文件包含)

目录 扫描 SMB 提权 扫描 SMB 让我们使用SMBMAP检查这些目录的权限。 smbmap -d workgroup -H www.example.com 可能/匿名帐户可访问。 使用smb尝试连接共享网络以访问/anonymous目录。[smb://192.168.59。129/]

ETL工具 - Kettle 介绍及基本使用

一、Kettle 介绍 在介绍 Kettle 前先了解下什么是 ETL&#xff0c;ETL是 Extract-Transform-Load 的缩写&#xff0c;即数据 抽取、转换、装载 的过程&#xff0c;对于企业或行业应用来说&#xff0c;经常会遇到各种异构数据的处理、转换、迁移等操作&#xff0c;这些操作有可…

java+mysql医院住院挂号缴费病房信息管理系统

手续办理&#xff1a;办理病人入院登记&#xff1b;提供病案首页建立与打印&#xff1b;交纳预交金及日结管理&#xff0c;并打印收据凭证&#xff1b;空床查询与统计&#xff1b;查询患者的住院信息&#xff1b;打印清单&#xff1b;出入院统计。 护士工作站&#xff1a;提供病…

Java每日一练(20230429)

目录 1. 二叉树的后序遍历 &#x1f31f;&#x1f31f; 2. 删除无效的括号 &#x1f31f;&#x1f31f;&#x1f31f; 3. 合并两个有序链表 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每…

【Java笔试强训 1】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f9be;&#x1f9be;&#x1f9be; 目录 一、选择题 二、编程题 &#x1f525;组队竞…

基于虚拟同步发电机的光伏混合储能并网系统MATLAB仿真

资源地址&#xff1a; 主要模块&#xff1a; 光伏电池模型&#xff08;按照数学模型搭建&#xff09;、蓄电池储能模块、超级电容储能模块、双向DC/DC模块、LC滤波器、逆变器VSG控制模块电压电流双环控制模块、光伏MPPT控制模块、储能系统充放电控制模块。 使用MATLAB2021b及…

2023/04/24 ~ 25 刷题记录

A - Sort the Subarray 大致题义&#xff1a;Monocarp有一个包含n个整数的数组a。他决定选择两个整数l和r&#xff0c;使1< 2rn&#xff0c;然后对子数组进行排序。子数组a[1 ..]R]是数组a中包含元素a1, al1, al2&#xff0c;…的部分。&#xff0c; ar-1, ar)按非降序排列。…

【STM32】知识补充 锁相环原理与应用解析

【STM32】知识补充 锁相环原理与应用解析 概述什么是锁相环 (PPL)锁相环的基本组成锁相环的工作原理锁相环应用STM32 中锁相环的应用STM32 配置锁相环总结 概述 锁相环 (Phase-Locked Loop) 在现代电子与通信系统中, 扮演着至关重要的角色. 凭借其独特的同步和频率调整能力, 锁…

大数据Doris(七):BE扩缩容

文章目录 BE扩缩容 一、BE扩容(创建BE与FE关系) 二、BE缩容 三、BE扩缩容注意问题

【操作系统复习】第5章 存储器管理 2

分页存储管理方式 页号P ◆12-31位&#xff1a;20位 ◆地址空间最多允许有1M&#xff08;2 20&#xff09;页 位移量W&#xff08;页内地址&#xff09; ◆0-11&#xff1a;12位 ◆每页大小为4KB &#xff08;2 12&#xff09; 对某特定机器&#xff0c;地址结构是一…

LangChain 2 ONgDB:大模型+知识图谱实现领域知识问答

LangChain 2 ONgDB&#xff1a;大模型知识图谱实现领域知识问答 LangChain 2 ONgDB&#xff1a;大模型知识图谱实现领域知识问答系统截图LangChain代理流程 Here’s the table of contents: LangChain 2 ONgDB&#xff1a;大模型知识图谱实现领域知识问答 LangChain 是一种 LL…

社区团购小程序怎么做,全流程解析

在当前的电商市场中&#xff0c;社区团购已经成为了一股强劲的力量。社区团购小程序作为社区团购的重要组成部分&#xff0c;已经成为了商家和消费者不可或缺的工具。社区团购小程序以其方便、快捷、实惠的特点&#xff0c;受到越来越多的用户的青睐&#xff0c;成为了电商市场…

只需5分钟,深刻理解本地事务状态表方案|分布式事务系列(四)

之前我们已经讲过了最基础的CAP、BASE理论&#xff0c;然后介绍了强一致性方案XA、2PC和3PC&#xff0c;然后详细讲述了TCC在生产中的应用场景和原理。本文继续讲解最终一致性方案——本地事务状态表方案。 点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;…

Go基础篇:类型系统

目录 前言✨一、什么是类型&#xff1f;二、类型特性1、静态类型检查2、类型推断 三、类型别名和自定义类型1、类型别名2、自定义类型3、类型别名和自定义类型的区别 四、类型底层结构1、类型元数据2、其他描述信息3、uncommontype 五、小结 前言✨ 前段时间忙着春招面试&#…

JDK1.8下载、安装和环境配置教程

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f9be;&#x1f9be;​​​​​​​ 目录 window系统安装java 下载JDK 配置环境变量 …

算法记录 | Day45 动态规划

70.爬楼梯 &#xff08;进阶&#xff09; 改为&#xff1a;一步一个台阶&#xff0c;两个台阶&#xff0c;三个台阶&#xff0c;…&#xff0c;直到 m个台阶。问有多少种不同的方法可以爬到楼顶呢&#xff1f; 1阶&#xff0c;2阶&#xff0c;… m阶就是物品&#xff0c;楼顶…

文件的使用

文章目录 1.概念1.1定义&#xff1a;1.2分类1.3文件名 2.文件的使用2.1文件指针2.2开闭函数2.3顺序读写2.3.1何为读写2.3.2读写函数1.字符输出fputc&#xff08;输出到文件 写到文件中&#xff09;2.字符输入fgetc&#xff08;输入到程序 读到程序中&#xff09;3.文本行函数4.…

别争了,Excel和Access都靠边,WPS也退,划时代电子表格早已出现

电子表格&#xff0c;绝不仅仅只是你看到的样子&#xff01; 无论是Excel&#xff0c;还是与它抗衡30多年&#xff0c;不分上下的金山WPS&#xff0c;都没能开创出真正划时代意义的电子表格。 揭秘前让我们先简单回顾下电子表格的前世今生&#xff0c;等不及的可直接进度条下拉…

超写实虚拟人制作教程

最近突然吹起一阵虚拟人直播风潮&#xff0c;大概就是找一个虚拟人物&#xff0c;用主播的面部动作来驱动虚拟人来完成头部和表情动作&#xff0c;但我看大部分都是下载的UE5Unreal Eigine 5&#xff08;Epic公司出品的一款强大的3D创作平台&#xff0c;很多大型3A大作都是用其…