---
title: 编译型语言代码归一
description: "基于编译型语言开发的应用程序，其编译后得到可执行程序，可执行程序执行时依赖的指令是芯片架构相关的。因此，软件迁移后鲲鹏架构上的代码和x86架构的代码会不一致，代码归一需要使用合适的措施隔离不同架构代码，本节以编译型语言代表C/C++语言为例，分别从代码片段和文件两个方面说明归一方法。"
url: https://www.hikunpeng.com/document/detail/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0056.html
sourcePath: /source/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0056.html
indexId: 6336817f6b330d73b6d73533f6627d3afa85761c6f1d4acfaa2edb2a1ec6bd7665
---
# 编译型语言代码归一

基于编译型语言开发的应用程序，其编译后得到可执行程序，可执行程序执行时依赖的指令是芯片架构相关的。因此，软件迁移后鲲鹏架构上的代码和x86架构的代码会不一致，代码归一需要使用合适的措施隔离不同架构代码，本节以编译型语言代表C/C++语言为例，分别从代码片段和文件两个方面说明归一方法。

#### 代码段编译宏控制

针对同一个文件内函数级别不同架构的代码，一般采用编译宏来区分隔离，编译宏可以使用GCC编译器自带的，也可以用自定义的编译宏。

处理步骤：

示例：

下面示例代码是CRC指令在x86和鲲鹏平台下的不同实现方法，可以通过编译宏控制来进行区分隔离，代码可以放到同一个文件中，实现代码归一。

- x86环境下会编译如下代码块：

```
#ifdef __x86_64__
static inline uint32_t crc32_u8(uint32_t crc, uint8_t v) {
__asm__("crc32b %1, %0" : "+r"(crc) : "rm"(v));
return crc;
}
static inline uint32_t crc32_u16(uint32_t crc, uint16_t v) {
__asm__("crc32w %1, %0" : "+r"(crc) : "rm"(v));
return crc;
}
static inline uint32_t crc32_u32(uint32_t crc, uint32_t v) {
__asm__("crc32l %1, %0" : "+r"(crc) : "rm"(v));
return crc;
}
static inline uint32_t crc32_u64(uint32_t crc, uint64_t v) {
uint64_t result = crc;
__asm__("crc32q %1, %0" : "+r"(result) : "rm"(v));
return result;
}
#endif
```


- 鲲鹏环境下会编译如下代码块：

```
#ifdef __aarch64__
static inline uint32_t crc32_u8(uint32_t crc, uint8_t value) {
__asm__("crc32cb %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value));
return crc;
}
static inline uint32_t crc32_u16(uint32_t crc, uint16_t value) {
__asm__("crc32ch %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value));
return crc;
}
static inline uint32_t crc32_u32(uint32_t crc, uint32_t value) {
__asm__("crc32cw %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value));
return crc;
}
static inline uint32_t crc32_u64(uint32_t crc, uint64_t value) {
__asm__("crc32cx %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value));
return crc;
}
#endif
```


C/C++语言中通过条件编译#ifdef，#endif等关键词实现将代码块编译区分，示例代码中采用了__x86_64__宏控制x86环境下运行的代码，采用了__aarch64__宏控制ARM环境下运行的代码，这两个宏是GCC预定义好的通用宏，GCC根据编译所在服务器的架构自行激活对应的宏，用户无需定义，使用比较方便。

各编译器支持用户自定义的宏，通过编译命令行带入传递到代码编译中，如，GCC通过添加 -DDEFINES或 -DDEFINES=CONDITION参数来定义用户的宏。

自定义宏和代码应用示例：

```
#include <stdio.h>
int main(void) {
#ifdef MY_X86_MAC
printf("Running in x86 model. \n");
#endif
#if MY_AARCH64_MAC
printf("Running in Kunpeng model. \n");
#endif
return 0;
}
```

将上述示例代码保存到test.c文件中，MY_X86_MAC和MY_AARCH64_MAC分别是自定义的控制x86和鲲鹏代码块的宏，利用GCC编译定义不同的宏参数，可以实现不同架构代码段：

执行gcc -DMY_X86_MAC test.c -o demo1编译后，运行demo1程序会显示：

```
Running in x86 model.
```

执行gcc -DMY_AARCH64_MAC test.c -o demo2编译后，运行demo2程序会显示：

```
Running in Kunpeng model.
```

至此，无论是GCC自带的宏还是自定义的编译宏，都可以实现通过编译宏方式对不同架构代码片段进行隔离，放到同一个代码文件中实现归一。


#### 编译文件makefile控制

如果业务代码中涉及不同架构的代码较多，使用代码片段隔离方式操作繁琐，易读性差，推荐将同一架构相关的代码放到同一个文件中，使用文件隔离的方式并采用makefile分文件编译方式实现代码归一。

处理步骤：

根据业务代码逻辑，将同一架构相关的代码放到同一个文件中，并做好目录和命名区分文件，修改makefile文件实现不同架构下编译对应的依赖文件实现归一。makefile可设置多个伪目标，不同目标实现对不同平台架构代码文件的编译，使用不同的make命令参数即可。更进一步通过脚本判断编译服务器架构类型，自动完成相应的编译调用，无需用户去判断平台架构。

示例：

假如两个代码文件src_x86.c、src_aarch64.c分别存储了x86架构和鲲鹏架构下的源代码，下面makefile文件示例可以区分编译不同的C文件代码。

通过伪目标区分不同架构编译makefile代码片段示例：

```
# other codes
.PHONY x86 aarch64
x86:src_x86.c
gcc  c1.c -o test
arm:src_aarch64.c
gcc  src_aarch64.c -o test
# other codes
```

这是一个简单的示例，运行make x86 编译器会对src_x86.c进行编译，运行make aarch64编译器对src_aarch64.c进行编译，都会生成test可执行文件。示例代码阐述了核心思想，实际项目中可以增加更多的源文件或者其他选项。

可以通过脚本自动识别芯片架构类型并执行不同的编译命令，高效不易出错，如下脚本示例可以完成自动判断编译：

```
# 获取芯片类型
ARCH_TYPE=`lscpu | grep Archit | awk '{print $2}'`
echo ${ARCH_TYPE}
# 如果是鲲鹏架构执行make aarch64编译
if [ "$ARCH_TYPE" = "aarch64" ]; then
make aarch64
fi
# 如果是x86架构执行make x86 编译
if [ "$ARCH_TYPE" = "x86_64" ];then
make x86
fi
```

将上述代码保存到build.sh文件中，执行sh build.sh自动完成编译，用户不感知芯片架构对应编译代码的差异。

源代码分文件存放，makefile中根据芯片类型区别编译的方法，保持很好的代码可维护性，是同一套代码适配多种平台的较为通用的方法之一。
