下面以 isa-l中 ec_encode_data()函数为例,弄清楚该函数对应的硬件优化版本的调用路径。
这里面涉及两个重要的步骤:接口定义、函数选择。之所以要进行函数选择,是因为不同的 CPU feature对应了不同的实现,具体来说就是,根据 CPU是否支持 SSE4.2、AVX2、AVX512等 feature,选择不同的硬件指令实现。
isa-l/erasure_code/ec_multibinary.asm文件中,存在如下语句:mbin_interface ec_encode_data
其中,宏 mbin_interface定义在 isa-l/include/multibinary.asm中,内容如下:
;;;;
; multibinary macro:
; creates the visable entry point that uses HW optimized call pointer
; creates the init of the HW optimized call pointer
;;;;
%macro mbin_interface 1
;;;;
; *_dispatched is defaulted to *_mbinit and replaced on first call.
; Therefore, *_dispatch_init is only executed on first call.
;;;;
section .data
%1_dispatched:
mbin_def_ptr %1_mbinit
section .text
mk_global %1, function
%1_mbinit:
endbranch
;;; only called the first time to setup hardware match
call %1_dispatch_init
;;; falls thru to execute the hw optimized code
%1:
endbranch
jmp mbin_ptr_sz [%1_dispatched]
%endmacro
进行宏替换后可知,当调用 ec_encode_data后,函数跳转至 ec_encode_data_dispatched处执行,在第一次调用 ec_encode_data时,ec_encode_data_dispatched处的函数为 ec_encode_data_mbinit,该函数将根据硬件特征设置最合适的硬件指令优化函数,具体工作由 ec_encode_data_dispatch_init完成。
isa-l/erasure_code/ec_multibinary.asm文件中,存在如下语句:mbin_dispatch_init6 ec_encode_data, \
ec_encode_data_base, \
ec_encode_data_sse, \
ec_encode_data_avx, \
ec_encode_data_avx2, \
ec_encode_data_avx512
上面有意敲了回车,是为了看的更清楚一些。上面第 2到 6行的 5个函数均是 ec_encode_data的备选函数。值得注意的是,第一个备选函数 ec_encode_data_base是不带硬件指令优化的C语言版本。其中,宏 mbin_dispatch_init6定义在 isa-l/include/multibinary.asm文件中,内容如下:
;;;;;
; mbin_dispatch_init6 parameters
; 1-> function name
; 2-> base function
; 3-> SSE4_2 or 00/01 optimized function
; 4-> AVX/02 opt func
; 5-> AVX2/04 opt func
; 6-> AVX512/06 opt func
;;;;;
%macro mbin_dispatch_init6 6
section .text
%1_dispatch_init:
push mbin_rsi
push mbin_rax
push mbin_rbx
push mbin_rcx
push mbin_rdx
push mbin_rdi
lea mbin_rsi, [%2 WRT_OPT] ; Default - use base function
mov eax, 1
cpuid
mov ebx, ecx ; save cpuid1.ecx
test ecx, FLAG_CPUID1_ECX_SSE4_2
je _%1_init_done ; Use base function if no SSE4_2
lea mbin_rsi, [%3 WRT_OPT] ; SSE possible so use 00/01 opt
;; Test for XMM_YMM support/AVX
test ecx, FLAG_CPUID1_ECX_OSXSAVE
je _%1_init_done
xor ecx, ecx
xgetbv ; xcr -> edx:eax
mov edi, eax ; save xgetvb.eax
and eax, FLAG_XGETBV_EAX_XMM_YMM
cmp eax, FLAG_XGETBV_EAX_XMM_YMM
jne _%1_init_done
test ebx, FLAG_CPUID1_ECX_AVX
je _%1_init_done
lea mbin_rsi, [%4 WRT_OPT] ; AVX/02 opt
;; Test for AVX2
xor ecx, ecx
mov eax, 7
cpuid
test ebx, FLAG_CPUID7_EBX_AVX2
je _%1_init_done ; No AVX2 possible
lea mbin_rsi, [%5 WRT_OPT] ; AVX2/04 opt func
;; Test for AVX512
and edi, FLAG_XGETBV_EAX_ZMM_OPM
cmp edi, FLAG_XGETBV_EAX_ZMM_OPM
jne _%1_init_done ; No AVX512 possible
and ebx, FLAGS_CPUID7_EBX_AVX512_G1
cmp ebx, FLAGS_CPUID7_EBX_AVX512_G1
lea mbin_rbx, [%6 WRT_OPT] ; AVX512/06 opt
cmove mbin_rsi, mbin_rbx
_%1_init_done:
pop mbin_rdi
pop mbin_rdx
pop mbin_rcx
pop mbin_rbx
pop mbin_rax
mov [%1_dispatched], mbin_rsi
pop mbin_rsi
ret
%endmacro
进行宏替换后可知,ec_encode_data_dispatch_init将根据 CPU feature从以下几个函数中选出最合适的一个:
ec_encode_data_base,
ec_encode_data_sse,
ec_encode_data_avx,
ec_encode_data_avx2,
ec_encode_data_avx512
同时,将其值保存在 mbin_rsi中。接着,在 ec_encode_data_init_done里面,通过
mov [%1_dispatched], mbin_rsi语句更新了 ec_encode_data_dispatched的值。
ec_encode_data_mbinit函数执行完后,又调用了 ec_code_data函数,此时还是跳转至 ec_encode_data_dispatched处执行,但由于在第一次调用 ec_code_data后,ec_encode_data_dispatched的值被更新了,因此在第二次以及后续调用 ec_encode_data_dispatched时,执行的都是硬件优化版本的 ec_code_data。