mirror of
https://github.com/janishutz/eth-summaries.git
synced 2026-01-13 14:58:30 +00:00
[SPCA] Improve Stack section
This commit is contained in:
12
semester3/spca/code-examples/01_asm/06_factorial.s
Normal file
12
semester3/spca/code-examples/01_asm/06_factorial.s
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
factorial:
|
||||||
|
pushq %rbx # Preserve frame pointer
|
||||||
|
movl %edi, %ebx
|
||||||
|
movl $1, %eax
|
||||||
|
cmpl $1, %edi
|
||||||
|
jle .QUIT # Base case reached: quit
|
||||||
|
leal -1(%rdi), %edi # Prepare args for next function call
|
||||||
|
call factorial
|
||||||
|
imull %ebx, %eax # Use result of function call
|
||||||
|
.QUIT:
|
||||||
|
popq %rbx # Restore frame pointer
|
||||||
|
ret
|
||||||
15
semester3/spca/code-examples/01_asm/07_swap_and_sum.s
Normal file
15
semester3/spca/code-examples/01_asm/07_swap_and_sum.s
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
swap_and_sum:
|
||||||
|
movq %rbx, -16(%rsp) # Save %rbx
|
||||||
|
movslq %esi,%rbx # Save i (and extend)
|
||||||
|
movq %r12, -8(%rsp) # Save %r12
|
||||||
|
movq %rdi, %r12 # Save a
|
||||||
|
leaq (%rdi,%rbx,8), %rdi # & a[i] -> %rdi (arg 1)
|
||||||
|
subq $16, %rsp # Allocate stack frame
|
||||||
|
leaq 8(%rdi), %rsi # & a[i+1] -> %rsi (arg 2)
|
||||||
|
call swap
|
||||||
|
movq (%r12,%rbx,8), %rax # a[i]
|
||||||
|
addq %rax, sum(%rip) # sum += a[i]
|
||||||
|
movq (%rsp), %rbx # Restore %rbx
|
||||||
|
movq 8(%rsp), %r12 # Restore %r12
|
||||||
|
addq $16, %rsp # Deallocate stack frame
|
||||||
|
ret
|
||||||
@@ -4,4 +4,4 @@ We then also store a reference / identifier \texttt{A} to the array (i.e. simila
|
|||||||
element of the array and can then be used in conjunction with ``assembly pointer arithmetic''.
|
element of the array and can then be used in conjunction with ``assembly pointer arithmetic''.
|
||||||
|
|
||||||
Array loops that are written as for-loops in code are usually transformed into do-while loops by the compiler to save one condition check in the beginning,
|
Array loops that are written as for-loops in code are usually transformed into do-while loops by the compiler to save one condition check in the beginning,
|
||||||
except of course, it might be possible that the loops is never executed.
|
except of course, it might be possible that the loop is never executed.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
\subsubsection{Structures}
|
\subsubsection{Structures}
|
||||||
We again allocate a contiguous region of memory.
|
We again allocate a contiguous region of memory.
|
||||||
Only now, the number of bytes required isn't as straight forward to compute anymore, it is still not hard:
|
Only now, the number of bytes required isn't as straightforward to compute anymore, but still relatively simple:
|
||||||
We simply sum up the sizes of all members and that will be our required sizes, so for the $n$ members $x_i$ of struct \texttt{my\_struct}, we have
|
We simply sum up the sizes of all members and that will be our required sizes, so for the $n$ members $x_i$ of struct \texttt{my\_struct}, we have
|
||||||
$\texttt{sizeof(my\_struct)} = \sum_{i = 0}^{n - 1} \texttt{sizeof}(x_i)$.
|
$\texttt{sizeof(my\_struct)} = \sum_{i = 0}^{n - 1} \texttt{sizeof}(x_i)$.
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
\newpage
|
\newpage
|
||||||
\subsection{The Stack}
|
\subsection{The Stack}
|
||||||
In the below two, we can do this with $x = 1, 2, 4, 8$, each corresponding to a size prefix that is set with \texttt{X}
|
The Stack is the main way to dynamically allocate memory in Assembly, i.e. for temporary values. This process is completely manual: Allocating/Deallocating memory on the stack is entirely explicit. The stack grows \textit{downwards}: Addresses decrease as the stack grows.
|
||||||
|
The Stack pointer \verb|%rsp| points to the current top of the stack.
|
||||||
|
|
||||||
|
\content{Using the Stack} \verb|pushX|, \verb|popX| exist for $x = 1, 2, 4, 8$, each corresponding to a size prefix that is set with \texttt{X}
|
||||||
|
|
||||||
\bi{Stack push} \texttt{pushX src}: Fetch operand at \texttt{src}, decrement \texttt{\%rsp} by $x$, then writes the operand at address of \texttt{\%rsp}
|
\bi{Stack push} \texttt{pushX src}: Fetch operand at \texttt{src}, decrement \texttt{\%rsp} by $x$, then writes the operand at address of \texttt{\%rsp}
|
||||||
|
|
||||||
\bi{Stack pop} \texttt{popX dest}: Fetch operand at address of \texttt{\%rsp}, increment \texttt{\%rsp} by $x$, then writes the operand into \texttt{dest}
|
\bi{Stack pop} \texttt{popX dest}: Fetch operand at address of \texttt{\%rsp}, increment \texttt{\%rsp} by $x$, then writes the operand into \texttt{dest}
|
||||||
|
|
||||||
\content{Procedure call / return} Use \texttt{call LABEL}. This pushes return label to the stack and jumps to the LABEL.
|
So intuitively, \verb|pushX| and \verb|popX| do exactly what is expected.
|
||||||
|
|
||||||
|
\content{Procedure call / return} Use \texttt{call LABEL}. This pushes the return label to the stack and jumps to \verb|LABEL|.
|
||||||
After this instruction, we also may use the \texttt{pushX} instruction to store further registers.
|
After this instruction, we also may use the \texttt{pushX} instruction to store further registers.
|
||||||
Just remember to pop in the correct order with the correct size again!
|
Just remember to pop in the correct order with the correct size again!
|
||||||
|
|
||||||
The \texttt{ret} instruction is the return instruction and it will jump back to the caller and execution will continue there.
|
The \texttt{ret} instruction is the return instruction and it will jump back to the caller and execution will continue there.
|
||||||
|
|
||||||
\subsubsection{Calling Conventions}
|
\subsubsection{Calling Conventions}
|
||||||
The callee is the function that is called and the caller is the code / function that calls the function.
|
Even though Assembly will never stop you from using registers in any way, \hlhref{cs61.seas.harvard.edu/site/pdf/x86-64-abi-20210928.pdf}{System V ABI}\ specifies a few conventions, standardizing especially how functions are allowed to use registers.
|
||||||
|
|
||||||
|
\content{Caller/Callee} The callee is the function that is called and the caller is the code / function that calls the function.
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \texttt{\%rax} and \texttt{\%eax} can be used without first saving (usually used as return)
|
\item \texttt{\%rax} and \texttt{\%eax} are caller saved (usually used as return)
|
||||||
\item Argument registers are caller saved (or not if not needed again)
|
\item \texttt{\%rdi, \%rsi, \%rdx, \%rcx} are caller saved (usually used for arguments)
|
||||||
\item \texttt{\%rsp} should not be modified anyway
|
\item \texttt{\%rsp} should not be modified manually
|
||||||
\item \texttt{\%rbp} is callee saved and is used as frame pointer (usually set to equal \texttt{\%rsp} at start of procedure and can be used to access elements of the frame
|
\item \texttt{\%rbp} is callee saved and used as frame pointer: usually set to \texttt{\%rsp} at start of procedure and used to access frame elements
|
||||||
(as it should not change during execution of the function and should always point to the start of the frame))
|
(should always point to the start of the frame during function)
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
|
\content{Register Conventions}
|
||||||
|
|
||||||
\begin{multicols}{2}
|
\begin{multicols}{2}
|
||||||
\begin{tables}{ll}{Name & Description}
|
\begin{tables}{ll}{Name & Description}
|
||||||
\texttt{\%rax} & Return value, \#variable args \\
|
\texttt{\%rax} & Return value, \#variable args \\
|
||||||
@@ -48,5 +58,21 @@ If we have more than 6 arguments to be passed, we can use the stack for this.
|
|||||||
If we can do all accesses to the stack relative to the stack pointer, we do not need to
|
If we can do all accesses to the stack relative to the stack pointer, we do not need to
|
||||||
update \texttt{\%rbp} and not even \texttt{\%rbx}, or we can use it for other purposes.
|
update \texttt{\%rbp} and not even \texttt{\%rbx}, or we can use it for other purposes.
|
||||||
|
|
||||||
We can also allocate the entire stack frame immediately by incrementing the stack pointer to the final position and then store data relative to it.
|
\content{Manual stack management} We can also allocate the entire stack frame directly by incrementing \texttt{\%rsp} to the final position and then store data relative to it.
|
||||||
To deallocate a stack frame, simply increment the stack pointer.
|
To deallocate a stack frame, simply increment the stack pointer.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Examples}
|
||||||
|
|
||||||
|
The stack is commonly used for recursive functions. A recursive factorial function might compile like this:
|
||||||
|
|
||||||
|
Note how \texttt{\%rbx} is saved on the stack, since \texttt{rbx} is callee-saved by convention. \\
|
||||||
|
\texttt{\%eax} is used directly, since it is caller-saved by convention.
|
||||||
|
|
||||||
|
\inputcodewithfilename{gas}{code-examples/01_asm/}{06_factorial.s}
|
||||||
|
|
||||||
|
A more complex example, passing addresses as arguments: \\
|
||||||
|
This function swaps 2 array elements (using a \texttt{swap} function) and adds the first value to an accumulator.
|
||||||
|
|
||||||
|
\inputcodewithfilename{gas}{code-examples/01_asm/}{07_swap_and_sum.s}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
\documentclass{article}
|
\documentclass{article}
|
||||||
|
|
||||||
\input{~/projects/latex/dist/full.tex}
|
\input{C:/projects/latex/dist/full.tex}
|
||||||
|
|
||||||
\renewcommand{\authorTitle}{Robin Bacher, Janis Hutz\\\url{https://github.com/janishutz/eth-summaries}}
|
\renewcommand{\authorTitle}{Robin Bacher, Janis Hutz\\\url{https://github.com/janishutz/eth-summaries}}
|
||||||
\renewcommand{\authorHeaders}{Robin Bacher, Janis Hutz}
|
\renewcommand{\authorHeaders}{Robin Bacher, Janis Hutz}
|
||||||
|
|||||||
Reference in New Issue
Block a user