C or C++ is a static compilation language. When you compile C or C++ source code, the source code is processed by compilers and assemblers to generate machine instructions. Then, the machine instructions are combined by linkers and library functions to generate executable programs. x86 platforms and ARM-based platforms use different instruction sets. If you want to migrate software from x86 platforms to ARM-based platforms, you must re-compile the software. This topic describes how to modify system macros and functions in C or C++ when you migrate software from x86 platforms to YiTian Elastic Compute Service (ECS) instances.
Macros
System macros are classified into the following types based on platform compatibility: macros that are defined for x86 platforms, macros that are defined for ARM-based platforms, and macros that are defined for both x86 and ARM-based platforms. The following table describes the macros that are defined for different platforms.
Macro for ARM-based platforms | Macro for x86 platforms | Macro for both x86 and ARM-based platforms |
__arm__ | __amd64__ | __STDC_HOSTED__ |
__thumb__ | i386 | __INT64_TYPE__ |
__ARM_ARCH_4T__ | __k8__ | _LP64 |
__aarch64__ | __SSE2__ | __WCHAR_MAX__ |
built-in functions
Built-in functions in the GCC compiler provide simple and convenient features to facilitate the development of programs. Most function names contain the __builtin_
prefix. Built-in functions that work as expected in x86 platforms such as builtin_ia32_xxx may fail to compile in YiTian instances.
You must modify some built-in functions when you migrate software from x86 platforms to ARM-based platforms. For example, change the CRC-related builtin_ia32_crc32qi(a, b)
function in x86 platforms to the builtin_aarch64_crc32cb(a, b)
function in ARM-based platforms.
Intrinsic functions
Intrinsic functions are closely related to x86 CPU architectures and are used as an interface to call Single Instruction Multiple Data (SIMD) operations. When you migrate software from x86 platforms to YiTian instances, the main focus is to migrate SIMD intrinsic functions.
Perform the following steps to migrate intrinsic functions from x86 platforms to YiTian instances:
Add header files such as arm_neon.h and arm_sve.h to ensure that the intrinsic functions can work in ARM systems.
Replace the intrinsic functions. For more information, see Find the Best Architecture for you on the Arm Developer website.
In the preceding two steps, you must modify the instruction sets and data types to suit ARM-based platforms. Example:
Change m128 mm_load_ps in x86 platforms to float32x4 vld1q_f32.
x86 platforms support Advanced Vector Extensions (AVX) instruction sets that use 256-bit registers. AArch64 platforms support only 128-bit registers. In this case, you need to expand the instructions by using the same semantics. For example, replace m256d mm_add_ps with the vaddq_f32 and vaddq_f32 SIMD instructions that are used in AArch64 platforms.
YiTian instances support SVE instructions. However, we recommend that you do not use SVE intrinsic functions due to the special programming mode of the instances. You can refer to open source projects in the community to resolve the issue. For more information, see sse2neon.
Inline assembly
x86 platforms use different assembly instructions from ARM-based platforms. When you migrate assembly instructions from x86 platforms to YiTian instances, change the assembly instructions that are used for x86 platforms to the instructions that use the same semantics for AArch64 platforms. For example, change asm("bswap %0": "=r"(val): "0"(val))
to the asm("rev %[dst], %[src]": [dst]"=r"(val): [src]"r"(val))
SVE instruction that uses the same semantics. For more information, see How to Use Inline Assembly Language in C Code.
Sample code
The following section describes how to modify code to migrate C or C++ code from x86 platforms to YiTian instances:
Code before modification:
#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
/* GCC-compatible compiler, targeting x86/x86-64 */
#include <x86intrin.h>
#endif
uint32_t crc32_4k(uint32_t acc, char* buf) {
for (char* end = buf + 4096; buf < end; buf += 4) {
acc = __builtin_ia32_crc32si(acc, *(uint32_t*)buf);
}
return acc;
}
Code after modification:
#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
/* GCC-compatible compiler, targeting x86/x86-64 */
#include <x86intrin.h>
#elif defined(__GNUC__) && defined(__ARM_NEON)
/* GCC-compatible compiler, targeting ARM with NEON */
#include <arm_neon.h>
#if defined(__ARM_FEATURE_SVE)
#include <arm_sve.h>
#endif
#endif
uint32_t crc32_4k(uint32_t acc, char* buf) {
for (char* end = buf + 4096; buf < end; buf += 4) {
acc = __builtin_aarch64_crc32cw(acc, *(uint32_t*)buf);
}
return acc;
}