
说 x86 和 ARM“水火不容”主要是指为 x86 编译的机器码通常不能直接交给 ARM CPU 执行反过来也一样。它们并不是不能共存而是两套不同的“CPU 语言体系”。操作系统、编译器或翻译工具可以帮助它们协作。一、根本原因CPU 听懂的指令不同假设 C# 代码是intAdd(inta,intb){returnab;}经过 IL2CPP 和本机编译C# 代码 ↓ IL ↓ IL2CPP 生成 C ↓ 针对目标平台编译 ├── x86-64 机器码 └── ARM64 机器码虽然做的都是“加法”但最后得到的机器码不同。示意汇编; x86-64 add eax, ecx; ARM64 add w0, w0, w1这里的寄存器分配只是示意不代表同一 ABI 下的完整函数。CPU 执行程序时并不理解“加法”这个抽象意图。它读取的是一串二进制编码并按照自身指令集解释。就像x86 CPU按照 x86 字典解读字节 ARM CPU按照 ARM 字典解读字节把 x86 机器码交给 ARM可能会遇到非法指令也可能被误解为完全不同的操作。不是结果算得慢一点而是语言本身就不对。二、指令编码方式也不同以常见的x86-64 和 ARM64/AArch64为例项目x86-64ARM64指令长度可变长度最长可达 15 字节通常讨论的 A64 指令固定 4 字节通用寄存器16 个 64 位通用寄存器31 个通用整数寄存器另有相关特殊约定内存访问风格很多运算允许带内存操作数主要使用 load/store 访问内存指令编码历史兼容层次较多编码组织方式不同例如x86 可以用一条指令表达add eax, [某个地址]大意是从内存读取一个值 加到 eaxARM64 通常写成ldr w1, [x2] add w0, w0, w1先加载再计算。但不要由此推断x86 一条顶 ARM 两条所以一定更快。现代 CPU 会进行乱序执行、流水线调度x86 的复杂指令也常被解码成内部微操作。汇编指令数量不等于执行时间。三、不只机器码不同“函数之间怎么交接”也不同即使两边都有加法指令还需要约定函数参数放在哪里 返回值放在哪里 哪些寄存器由调用者保存 栈如何对齐 结构体怎么传递 异常怎样展开这些属于ABI应用二进制接口。比如常见调用约定中前几个整数参数可能放在Windows x64 RCX、RDX、R8、R9 Linux x86-64 的 System V ABI RDI、RSI、RDX、RCX、R8、R9 常见 ARM64 ABI X0X7这说明一个重要事实即使 CPU 架构相同不同操作系统的二进制程序也未必能够直接互用。程序能不能直接运行至少要看指令集是否支持 ABI 是否匹配 操作系统接口是否匹配 动态库等依赖是否齐全所以一个 Windows x86-64 DLL既不能直接当作 ARM64 动态库用也不能仅凭 CPU 相同就当作 Linux 动态库用。四、多线程代码还可能遇到“内存顺序差异”这是比较隐蔽的一层。假设线程 Adata42;readytrue;线程 Bif(ready){use(data);}程序员容易认为看见ready true就一定能看见data 42。但如果没有正确同步这个推理不成立。通常来说x86 提供相对较强的硬件内存顺序ARM 允许更宽松的内存顺序行为编译器也可能进行优化和重排。因此一些错误的并发代码可能在 x86 上“看起来没问题”到了 ARM 上才暴露。不过没有同步的 C 数据竞争本身就是未定义行为不能依赖 x86 的表现来证明正确。正确做法是使用锁原子操作acquire/release 等正确的同步语义。对于 C#应使用lock、Interlocked等适当机制由运行时或 IL2CPP 工具链落实到目标架构。五、那为什么有些 ARM 电脑能运行 x86 软件因为中间加了“翻译官”。1. 重新编译让程序直接讲 ARM 语言同一份源代码 ├── 编译为 x86-64 └── 编译为 ARM64这是最直接的原生适配方式。但如果代码包含以下内容仍需要修改x86 专用汇编SSE/AVX 等架构相关指令不可移植的内存布局假设只有 x86 版本的第三方库。2. 二进制翻译把 x86 指令换成 ARM 指令例如 Rosetta 2以及 Windows on Arm 的相关兼容机制。概念流程x86 指令块 ↓ 分析与转换 ↓ ARM 指令块 ↓ 执行并可能缓存转换结果难点不只是替换指令名称还包括寄存器映射标志位语义内存顺序异常行为向量指令与操作系统及原生库的交互。因此兼容性和性能会受具体程序影响。3. 模拟整台机器还可以模拟另一种 CPU甚至模拟设备和系统环境。但要区分虚拟化不等于跨架构翻译。同架构虚拟化可以让大量客户机指令直接在硬件上执行跨架构通常还需要模拟或翻译。六、对 Unity / IL2CPP 项目有什么实际影响假设你做一个游戏Game.cs ↓ IL2CPP ↓ 生成平台相关的本机程序你可以共用大量 C# 源码但通常需要分别生成Windows x86-64 Android ARM64 iOS ARM64 其他目标平台……最容易踩坑的是原生插件例如[DllImport(MyPlugin)]privatestaticexternintCalculate();C# 声明相同不代表插件二进制通用。你必须提供目标平台和架构对应的库Windows x86-64 → 对应的 DLL Android ARM64 → 对应的 .so iOS ARM64 → 对应的静态库或 Framework 等不仅指令集要匹配还要保证导出符号正确调用约定正确参数类型匹配结构体布局符合双方约定。IL2CPP 不会自动把你附带的 x86 原生插件变成 ARM 插件。七、不是“x86 高性能ARM 低功耗”这么简单这也是“水火不容”说法常带来的误解。传统市场印象是x86 → PC、服务器 ARM → 手机、嵌入式但如今 ARM 也用于桌面电脑和服务器x86 也有面向低功耗的产品。实际性能和功耗取决于微架构制程核心数量与频率缓存和内存系统向量能力功耗预算软件优化。指令集有影响但不能仅凭 x86 或 ARM 标签判断谁更快、更省电。总结x86 和 ARM 的“不兼容”可以归纳为三层第一层CPU 语言不同 → 机器码通常不能直接执行。 第二层二进制协作规则不同 → 函数调用、库和系统接口需要匹配。 第三层底层行为与优化方式不同 → 并发、汇编、SIMD 等代码需要适配。但在源代码层它们并不一定冲突同一份可移植代码可以分别编译给 x86 和 ARM同一份机器码则通常不能不经转换就跨架构运行。