arm和x86体系结构中的c/c++语言有什么不同

What differences in c/c++ language in arm and x86 architectures

本文关键字:语言 c++ 什么 x86 体系结构 arm      更新时间:2023-10-16

我正在尝试将最初为x86体系结构编写的代码移植到arm。

https://github.com/SDLash3D

最初它是为msvc/win32开发的,代码中有很多错误,所以可能有UB。

当使用gcc编译时,代码在x86上工作,但在arm上它有不同的行为。它似乎丢失了数组中的一些数据,武器开关也不起作用。它影响了服务器库和客户端库。

应用程序是单线程的,所以不存在同步问题。

Char在arm上默认是无符号的,所以我将-fsigned-char添加到编译器标志中,但它并没有解决问题。arm和x86c代码之间还有什么区别?

我试着用gcc和clang在arm上编译代码,没有区别,所以这不是编译器错误。

第S页我用gcc-4.9(而不是4.8)编译了x86的代码,得到了同样的行为。之后,我组合了两个编译器,发现问题出在net_encode.c中。

到那时,塞巴斯蒂安·切瓦利埃发现

iValue /= pField->multiplier; iValue *= pField->multiplier;

当iValue为整数且pField->multiplier==1.0f时,有时会更改整数值。

如果pField->multiplier!=乘法前1.0f。

如果没有看到您的代码,几乎不可能说出来,但会尝试一般地回答。

x86和ARM之间的一个巨大区别是,大多数x86指令都是内部原子指令,而ARM没有这样的东西——你必须明确地说"只执行这组指令"。因此,如果您有由多个线程更新的数据,这可能会影响您。

个别指令的行为方式也存在差异。在不知道代码的作用的情况下,很难说它是否会影响代码,但有一件事可能会发生,那就是"未对齐访问",它在x86中完全有效(尽管不是最佳),但在ARM处理器上无效(在大多数型号中)。因此指针必须与它访问的项的大小对齐。

当然,编译器中的代码生成是不同的,它们可能会根据输入代码做出不同的决定,从而最终产生在各种方面表现不同的代码。我不止一次想到的是"参数函数调用的执行顺序":

func(func1(), func2()); 

注意,func1()func2()可以首先执行。如果你依赖这样的订单,你需要做:

t1 = func1();
t2 = func2();
func(t1, t2); 

提示:

如果您还没有,请启用尽可能多的警告(至少是-Wall)。并修复任何警告

检查#pragma pack或类似的"打包数据结构",以及类似的从char *int *的强制转换等,因为这些可能会导致未对齐的访问问题。

编辑:当然,不同的编译器会有不同的错误,这些错误可能会对特定的编译器、处理器和代码组合产生影响,也可能不会产生影响。虽然叮当声的可能性不大,但也出现了同样的问题。

我不是这方面的专家,但我可以指出一些一般的事情。

首先,根据ARM的版本和设置,一些基元类型的大小、有符号性和字节序可能与x86不同(尤其是x86_64)。在这方面,编写良好的可移植代码永远不应该对这些方面做出任何假设。如果需要特定的大小和签名性,可以使用<cstdint>/<stdint.h>标头中的类型。endianness也是你应该注意的事情。

其次,正如Mats所提到的,ARM和x86之间最臭名昭著、最能改变行为的区别之一是内存模型的严格性。简而言之,x86往往非常保守(因此,本质上是"安全的",或者至少不足为奇),而ARM则弱得多(因此,可能更快)。这对于并发代码来说尤其重要。默认情况下,x86上的许多基本操作都是原子操作,并且有许多隐式的全内存围栏可以保证总体一致性。默认情况下,ARM上没有那么多保护机制,这可能会导致许多奇怪的行为。

例如,许多天真的多线程代码会错误地使用volatile变量来在线程之间共享信息或信号。在许多情况下,由于x86的内存模型保守,这在x86上可以正常工作。但在ARM体系结构上,该代码很可能已被破坏。

最后,一般来说,不同的体系结构在某些操作上的表现会略有不同。所有这些差异通常属于标准的"未定义行为"或"实施定义"类别。这些是最难发现的错误,因为未定义的行为通常意味着在一个架构(和编译器)上会发生一件事,这可能是可以的,但在另一个架构上,会发生其他事情,这可能不是可以的

有一些工具可以帮助您解决所有这些问题。主要工具是所谓的"消毒剂"。这些工具为您的运行时代码提供工具,将对与所有这些相关的各种问题进行大量检查(即,您可以使用正在工作的x86代码运行它们,它们会将您指向在ARM或其他地方可能表现不同的"危险"事物)。您可以尝试UBSan(用于UB操作的未定义行为清理程序)、TSan(用于数据争用和危险的跨线程内存访问的线程清理程序)和ASan(用于内存调试的地址清理程序),它们在Clang和GCC下都可用。我不知道这些工具是否有什么特别的东西可以帮助ARM,或者它们是否针对ARM,但至少,在将其移植到ARM时,修复它们在x86上指向的任何问题都会有很大帮助。