编译型语言代码归一

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

代码段编译宏控制

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

处理步骤:

示例:

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

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中根据芯片类型区别编译的方法,保持很好的代码可维护性,是同一套代码适配多种平台的较为通用的方法之一。