我对ARM编程很陌生。我注意到有几种架构,例如ARMv4,ARMv5,ARMv6等。它们之间有什么区别?他们有不同的指令集或行为吗?
最重要的是,如果我为ARMv6编译了一些C代码,它将在ARMv5上运行吗?在ARMv6上运行的ARMv5代码又如何呢?还是如果我正在编写内核汇编代码,我是否只需要担心差异?
最佳答案
ARM的世界有点困惑。
对于C程序员而言,事情很简单:所有ARM体系结构都提供常规的32位平面寻址编程模型。只要您使用C源代码,您可能会看到的唯一区别就是字节顺序和性能。大多数ARM处理器(甚至是旧型号)都可以是big-endian和little-endian。然后由逻辑板和操作系统进行选择。良好的C代码是Endian中性的:无论平台的字节性如何,它都能正确编译和工作(Endian中性既可提高可靠性和可维护性,又可提高性能:非中性代码是通过不同大小的指针访问相同数据的代码,这会严重破坏编译器用来优化代码的严格别名规则。
如果您考虑与二进制兼容性(即重复使用已编译一次的代码),情况就大不相同了:
给定的处理器可以实现几个指令集。仅知道ARM代码的最新处理器是StrongARM,它是已经相当老(15年)的ARMv4代表。 ARM7TDMI(ARMv4T架构)同时知道ARM和Thumb,除了Cortex-M之外,几乎所有其他后续ARM系统也都知道。只要在更改约定的地方插入适当的胶,ARM和Thumb代码就可以在同一应用程序中混合在一起。这称为拇指交互操作,可以由C编译器自动处理。
Cortex-M0仅知道Thumb指令。它知道一些扩展,因为在“常规” ARM处理器中,操作系统必须使用ARM代码(用于处理中断)。因此,Cortex-M0知道一些Thumb-for-OS的知识。这对于应用程序代码无关紧要。
另一个Cortex-M仅知道Thumb-2。 Thumb-2至少在组装级别上与Thumb大部分向后兼容。
因此,如果使用编译器开关编译了某些代码,表明该代码是针对ARMv6的,则编译器可能会使用ARMv6具有但不属于ARMv5的少数指令之一。这是几乎在所有平台上都遇到的一种常见情况:例如,如果您使用
-march=core2
标志在带有GCC的PC上编译C代码,则生成的二进制文件可能无法在较旧的Pentium处理器上运行。调用约定是一组规则,用于指定函数如何交换参数和返回值。处理器仅知道其寄存器,没有堆栈的概念。调用约定说明了哪些寄存器参数进入以及如何编码(例如,如果有
char
参数,它将进入寄存器的低8位,但是调用方应清除/符号扩展高24位) , 或不 ?)。它描述了堆栈的结构和对齐方式。它将结构条件的对齐条件和填充标准化。ARM有两种主要的约定,称为ATPCS(旧)和AAPCS(新)。在浮点值的主题上,它们是完全不同的。对于整数参数,它们几乎是相同的(但AAPCS需要更严格的堆栈对齐)。当然,约定根据指令集和Thumb互通的存在而有所不同。
在某些情况下,可能有一些既符合ATPCS又符合AAPCS的二进制代码,但是这是不可靠的,也不会发出不匹配的警告。因此,底线是:使用不同的调用约定的系统之间不能具有真正的二进制兼容性。
可以使用可选元素扩展ARM体系结构,这些元素将自己的指令添加到核心指令集。 FPU是这样的可选协处理器(在实践中很少遇到)。另一个协处理器是NEON,这是一些较新的ARM处理器上的SIMD指令集。
使用协处理器的代码将不会在不具有该协处理器功能的处理器上运行,除非操作系统捕获相应的操作码并在软件中模拟该协处理器(这几乎是使用ATPCS调用时浮点参数所发生的情况)惯例,而且很慢)。
综上所述,如果您有C代码,请重新编译它。不要尝试重复使用为另一体系结构或系统编译的代码。
关于c - 从C程序员的角度来看,ARM体系结构之间的差异?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4381102/