ARMv8 Cortex 您所在的位置:网站首页 armv7和armv8 ARMv8 Cortex

ARMv8 Cortex

#ARMv8 Cortex| 来源: 网络整理| 查看: 265

/* TODO 本系列文章是对 ARMv8 Cortex-a 系列编程向导手册拙劣的翻译和注解,若有出入,以官方文档为准 */

Chapter 8 移植到 A64

这一章节不打算对如何编写可移植代码做出详细介绍,而只介绍应用工程师在编写可移植代码时的主要应该关注的方面。 当移动 A32 代码到 A64 架构上运行时,我们应该清晰的认识到 A64 指令集与 A32/T32 指令集存在下面这些重大的差异:

大多数 A32 指令可以条件执行,但是在 A64 指令集中,只有少数指令可以条件执行。大多数 A64 指令可以应用一个任意的偏移量到源寄存器。A64 指令集的寻址模式与 A32 不同。A32、T32 指令集使用的偏移寻址、索引前/索引后寻址在 A64 中依然可用,但是,A64 中新增了 PC 相关寻址模式,因为在 A64 中, PC 不能作为一个通用目的寄存器被访问。A64 指令集移除了所有的多内存访问指令,但是添加了 LDP 与 STP 指令来加载、存储寄存器对,这两个指令可以操作任意两个寄存器,A64 同样移除了 PUSH 和 POP 指令。ARMv8 新增了包含单向内存屏障的加载与存储指令:load-acquire 与 store-release。load-acquire 指令要求接下来任意的内存访问只有在 load-acquire 完成之后才可见;store-release 确保了所有之前的内存访问在 store-release 指令可见之前可见。A64 指令集不支持协处理器的访问,比如 CP15。在 AArch64 状态下,不存在 CPSR 寄存器,CPSR 功能包含在 PSTATE 位域中,可以通过特殊目的寄存器来访问 PSTATE 位域 。

对于大多数应用,当代码移植到 AArch64 架构上运行时,仅仅需要重编译源代码。然而,在某些方面也存在一些不可移植的 C 代码。

8.1 对齐

数据和代码必须对齐合适的边界。 对齐可以影响 ARM 内核的性能,并且可以代表一个可以问题。 之前的 ARM 编译器语法使用 ALIGN n 指令来实现对齐,n 表示对齐的字节边界,比如 ALIGN 128 表示 128 字节对齐。 GNU 汇编语法与 ARM Complier 6 语法使用 .balign n 指令来实现对齐,同样,n 表示对齐的字节边界。 当我们移植 A32 代码到 AArch64 架构时,应该把 ALIGN n 指令更换为 .balign n 。

注意: GNU 汇编也提供一个 .align n 指令,但此时 n 的格式在不同架构的内核中具有不同的格式

8.2 数据类型

64 位机器上,大多数 C 编程环境中, int 类型依旧是 4 字节,但是,long 与指针类型是 8 字节宽。这种数据类型被称作 LP64 数据模型。 ARM ABI 为 LP64 数据模型定义了一些基本数据类型,如下:

在这里插入图片描述 在 AArch64 架构中,64 位数据可以更有效的被处理。int 类型依旧是 4 字节,指针类型是 8 字节。ARM ABI 默认 char 类型为 unsigned 。

如果应用代码没有对指针类型的不可移植操作,比如:将一个非指针类型的数,强制转换为一个指针类型,或者对指针做算数运算。那么代码移植到 AArch64 架构上运行是很简单的,只需要重编译源代码就好。 如果涉及当上述的强制类型转换与指针算数运算,那么我们需要慎重分析源代码,并消除每一个警告。

8.2.1 汇编代码

大多数 A32 汇编指令可以很简单的被 A64 指令所取代,比如下表: 在这里插入图片描述 在这里插入图片描述 但是,A64 指令集与 A32 指令集依旧在很多方面存在不同,这时,我们需要重新编写汇编代码,如下表: 在这里插入图片描述

注意:根据 64 位的 APCS, 堆栈指针需要 16 直接对齐

在 AArch64 架构中,我们通过访问 PSTATE 位域来代替对 CPSR 寄存器的访问: 在这里插入图片描述

8.3 当代码从 32 位环境移植到 64 位环境时的问题

当移植 C 代码到 64 位环境时,需要考虑如下的问题:

考虑 int 类型与 指针 类型长度,因为在某些芯片架构上,这两个类型长度可能不一致。64 位系统有更大的内存访问范围,int 类型的索引可能不能索引到数组所有的元素,导致死循环。C 表达式的隐式类型转换也许会造成意料之外的影响。不同数据类型与符号数之间的运算必须谨慎。因为会涉及到类型转换。 8.3.1 重编译或重写代码

任何代码的移植都需要重编译或重写代码,我们的目标是最大化前者,最小化后者。

由于基本数据类型的改变,建议谨慎思考代码编译过程中的每一个错误与警告。

最后,建议谨慎使用强制类型转换。

8.3.2 ARM Compiler 6 针对 ARMv8-A 的编译器选项 选项举例说明–target==aarch64-arm-none-eabi生成 ARMv8-A 架构的代码,使用 A64 指令集–target==armv8a-arm-none-eabi生成 ARMv8-A 架构的代码,使用 A32/T32 指令集–target==armv7a-arm-none-eabi生成 ARMv7-A 架构的代码,使用 A32/T32 指令集 8.4 对编写 C 代码的建议 分配内存时使用 sizeof ,而不是常量,比如: (void**) calloc(4,100) 替换为 (void**) calloc(sizeof(void *), 100) 如果需要用到强制类型转换,那么请使用 stdint.h 文件中的统一类型谨慎考虑结构体成员布局,因为存在结构体对齐这一原因。请一定知道任意的立即数都是 int 类型,请在移位操作中谨慎使用立即数,如果一定要使用,那么请按照如下方式: long value = 1L long long mask; mask = 1LL struct struct_A A1; A1.i0 = i0; A1.i1 = i1; A1.d0 = d0; A1.d1 = d1; return A1; } void bar() { AA = foo(0, 1, 1.0, 2.0); }

反汇编代码如下:

foo// SUB SP, SP, #0x30 STR W0, [SP, #0x2C] STR W1, [SP, #0x28] STR D0, [SP, #0x20] STR D1, [SP, #0x18] LDR W0, [SP, #0x2C] STR W0, [SP, #0] LDR W0, [SP, #0x28] STR W0, [SP, #4] LDR W0, [SP, #0x20] STR W0, [SP, #8] LDR W0, [SP, #0x18] STR W0, [SP, #10] LDR X9, [SP, #0x0] STR X9, [X8, #0] LDR X9, [SP, #8] STR X9, [X8, #8] LDR X9, [SP, #0x10] STR X9, [X8, #0x10] ADD SP, SP, #0x30 RET bar// STP X29, X30, [SP, #0x10]! MOV X29, SP SUB SP, SP, #0x20 ADD X8, SP, #8 MOV W0, WZR ORR W1, WZR, #1 FMOV D0, #1.00000000 FMOV D1, #2.00000000 BL foo: ADRP X8, {PC}, 0x78 ADD X8, X8, #0 LDR X9, [SP, #8] STR X9, [X8, #0] LDR X9, [SP, #0x10] STR X9, [X8, #8] LDR X9, [SP, #0x18] STR X9, [X8, #0x10] MOV SP, X29 LDP X20, X30, [SP], #0x10 RET

在上述的示例代码中,由于 foo() 返回的结构体超过 16 个字节,所以,返回结果需要保存在 X8 中。示例代码体现了下列通用寄存器的作用:

W0,W1,D0,D1 用于传递子函数的形参。bar() 在栈中开辟空间,并给 X8 赋值,foo() 将返回值保存在 X8 中。

AAPCS64 栈帧的使用如下图所示,X29(FP) 应该指向堆栈中的前一帧,并且保存在 LR 之后。 在这里插入图片描述

9.1.3 NEON 与浮点寄存器的使用

AArch64 包含了 32 个浮点寄存器 V0-V31,可以用于 NEON 与浮点运算。

AArch64 PCS 对浮点寄存器分类如下: 在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有