12.7: Example, Statistical Function2 (non-leaf)
- Page ID
- 58054
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)This extended example will demonstrate calling a simple void function to find the minimum, median, maximum, sum and average of an array of numbers.
The High-Level Language (HLL) call for C/C++ is as follows:
stats2(arr, len, min, med1, med2, max, sum, ave);
For this example, it is assumed that the array is sorted in ascending order. Additionally, for this example, the median will be the middle value. For an even length list, there are two middle values, med1 and med2, both of which are returned. For an odd length list, the single middle value is returned in both med1 and med2.
As per the C/C++ convention, the array, arr, is call-by-reference and the length, len, is call-by-value. The arguments for min, med1, med2, max, sum, and ave are all call-by- reference (since there are no values as yet). For this example, the array arr, min, med1, med2, max, sum, and ave variables are all signed double-word integers. Of course, in context, the len must be unsigned.
Caller
In this case, there are 8 arguments and only the first six can be passed in registers. The last two arguments are passed on the stack. The assembly language code in the calling routine for the call to the stats function would be as follows:
; stats2(arr, len, min, med1, med2, max, sum, ave); push ave ; 8th arg, add of ave push sum ; 7th arg, add of sum mov r9, max ; 6th arg, add of max mov r8, med2 ; 5th arg, add of med2 mov rcx, med1 ; 4th arg, add of med1 mov rdx, min ; 3rd arg, addr of min mov esi, dword [len] ; 2nd arg, value of len mov rdi, arr ; 1st arg, addr of arr call stats2 add rsp, 16 ; clear passed arguments
The 7th and 8th arguments are passed on the stack and pushed in reverse order in accordance with the standard calling convention. After the function is completed, the arguments are cleared from the stack by adjusting the stack point register (rsp). Since two arguments, 8 bytes each, were passed on the stack, 16 is added to the stack pointer.
Note, the setting of the esi register also sets the upper-order double-word to zero, thus ensuring the rsi register is set appropriately for this specific usage since length is unsigned.
No return value is provided by this void routine. If the function was a value returning function, the value returned would be in the A register.
Callee
The function being called, the callee, must perform the prologue and epilogue operations (as specified by the standard calling convention). Of course, the function must perform the summation of values in the array, find the minimum, medians, and maximum, compute the average, return all the values.
When call-by-reference arguments are passed on the stack, two steps are required to return the value.
- Get the address from the stack.
- Use that address to return the value.
A common error is to attempt to return a value to a stack-based location in a single step, which will not change the referenced variable. For example, assuming the double-word value to be returned is in the eax register and the 7th argument is call-by-reference and where the eax value is to be returned, the appropriate code would be as follows:
mov r12, qword [rbp+16] mov dword [r12], eax
These steps cannot be combined into a single step. The following code
mov dword [rbp+16], eax
Would overwrite the address passed on the stack and not change the reference variable. The following code implements the stats2 example.
; Simple example function to find and return the minimum, ; maximum, sum, medians, and average of an array. ; ----- ; HLL call: ; stats2(arr, len, min, med1, med2, max, sum, ave); ; Arguments: ; arr, address – rdi ; len, dword value – esi ; min, address – rdx ; med1, address - rcx ; med2, address - r8 ; max, address - r9 ; sum, address – stack (rbp+16) ; ave, address – stack (rbp+24) global stats2 stats2: push rbp ; prologue mov rbp, rsp push r12 ; ----- ; Get min and max. mov eax, dword [rdi] ; get min mov dword [rdx], eax ; return min mov r12, rsi ; get len dec r12 ; set len-1 mov eax, dword [rdi+r12*4] ; get max mov dword [r9], eax ; return max ; ----- ; Get medians mov rax, rsi mov rdx, 0 mov r12, 2 div r12 ; rax = length/2 cmp rdx, 0 ; even/odd length? je evenLength mov r12d, dword [rdi+rax*4] ; get arr[len/2] mov dword [rcx], r12d ; return med1 mov dword [r8], r12d ; return med2 jmp medDone evenLength: mov r12d, dword [rdi+rax*4] ; get arr[len/2] mov dword [r8], r12d ; return med2 dec rax mov r12d, dword [rdi+rax*4] ; get arr[len/2-1] mov dword [rcx], r12d ; return med1 medDone: ; ----- ; Find sum mov r12, 0 ; counter/index mov rax, 0 ; running sum sumLoop: add eax, dword [rdi+r12*4] ; sum += arr[i] inc r12 cmp r12, rsi jl sumLoop mov r12, qword [rbp+16] ; get sum addr mov dword [r12], eax ; return sum ; ----- ; Calculate average. cdq idiv rsi ; averge = sum/len mov r12, qword [rbp+24] ; get ave addr mov dword [r12], eax ; return ave pop r12 ; epilogue pop rbp ret
The choice of the registers is arbitrary with the bounds of the calling convention. The call frame for this function would be as follows:
In this example, the preserved registers, rbp and then r12, are pushed. When popped, they must be popped in the exact reverse order r12 and then rbp in order to correctly restore their original values.