下面以 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
。