Normalizing Code of Compiled Languages
After the applications written in compiled language are complied as executable programs, the instructions vary depending on the processor architecture. Therefore, after software porting, the code in the Kunpeng architecture is different from that in the x86 architecture. During code normalization, proper measures need to be taken to isolate the code of different architectures. This section uses the C and C++ as examples to describe the normalization methods of code snippets and files.
Using Compilation Macros to Isolate Code Snippets
Compilation macros are used to isolate the code with different function levels and architectures in a file. You can use the compilation macros provided by the GCC compiler or use customized compilation macros.
Procedure
Example:
The following example shows different implementation methods of CRC instructions on the x86 and Kunpeng platforms. The code in the same file can be distinguished and isolated by compiling macros to implement code normalization.
- Compile the following code in the x86 environment:
#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 - Compile the following code in the Kunpeng environment:
#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
In the C and C++ languages, differentiated code block compilation is implemented through conditional compilation of keywords such as #ifdef and #endif. In the example, the __x86_64__ macro is used to control the code running on x86, and the __aarch64__ macro is used to control the code running on ARM. The two macros are common macros defined by the GCC. The GCC automatically activates the corresponding macros based on the architecture of the server where the compilation is performed. You do not need to define the macros.
Compilers support user-defined macros, which are carried in the compile command lines and transferred to the code compilation. For example, macros can be customized by adding the -DDEFINES or -DDEFINES=CONDITION parameter for the GCC compiler.
Example of customized macros and code:
#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;
}
Save the preceding code to the test.c file. MY_X86_MAC and MY_AARCH64_MAC are user-defined macros that control the x86 and Kunpeng code, respectively. You can use GCC compiler to define different macro parameters to implement code of different architectures:
After gcc -DMY_X86_MAC test.c -o demo1 is executed for compilation, the following information is displayed when the demo1 program is run:
Running in x86 model.
After gcc -DMY_AARCH64_MAC test.c -o demo2 is executed for compilation, the following information is displayed when the demo2 program is run:
Running in Kunpeng model.
In this way, both the GCC built-in macros and user-defined macros can be used to isolate code snippets of different architectures in the same file for normalization.
Using makefile for Code Normalization
If a large amount of code of different architectures are involved, the code snippet isolation mode is complex and has drawbacks such as poor readability. You can place the code related to the same architecture in a file, isolate different files, and use makefile for compilation to implement code normalization.
Procedure
Place the code of different architectures in different files based on the service code logic, and name and save the files for easy identification. Modify the makefile file to compile the dependency files in different architectures to implement code normalization. The makefile can be set with multiple pseudo targets for compilation of code files of different platform architectures. The caller only needs to execute different make command parameters. Use a script to determine the architecture of the server and automatically complete the compilation and call of the response. Users do not need to determine the platform architecture.
Example:
Assume that src_x86.c and src_aarch64.c store the source code in the x86 architecture and Kunpeng architecture, respectively. The following makefile file can be used to differentiate and compile different C file code.
Example of distinguishing and compiling the makefile code of different architectures using pseudo targets:
# 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
In this example, the make x86 compiler is executed to compile src_x86.c, and the make AArch64 compiler is executed to compile src_aarch64.c. The test executable files are generated. This example illustrates the core idea. More source files or options can be added to the actual project.
A script can be used to automatically identify the chip architecture type and execute different compilation commands. The following is a script example:
...
# Obtains the chip type.
ARCH_TYPE=`lscpu | grep Archit | awk '{print $2}'`
echo ${ARCH_TYPE}
# If it is the Kunpeng architecture, run make aarch64 for compilation.
if [ "$ARCH_TYPE" = "aarch64" ];then
make aarch64
fi
# If it is the x86 architecture, run make x86 for compilation.
if [ "$ARCH_TYPE" = "x86_64" ];then
make x86
fi
...
}
Save the preceding code to the build.sh file and run the sh build.sh command to complete compilation. Users are unaware of the differences between the compiled code with the chip architecture.
The source code of different architectures is stored in different files. In makefile, the compilation methods are differentiated based on the chip type to ensure high code maintainability. It is one of the common methods for adapting the same set of code to multiple platforms.