C-x-s
authortwisti <none@none>
Fri, 30 Jul 2004 21:56:24 +0000 (21:56 +0000)
committertwisti <none@none>
Fri, 30 Jul 2004 21:56:24 +0000 (21:56 +0000)
doc/handbook/x86_64.tex

index c96cec673bc3ef8213420140ba65dd0ce2fbfdd1..0eb684fd5909d031f96ef2f9ec6ccdfa9848e5de 100644 (file)
@@ -5,7 +5,7 @@
 The AMD64 architecture, formerly know as x86\_64, is an improvement of
 the Intel IA32 architecture by AMD -- Advanced Micro Devices. The
 extraordinary success of the IA32 architecture and the upcoming memory
 The AMD64 architecture, formerly know as x86\_64, is an improvement of
 the Intel IA32 architecture by AMD -- Advanced Micro Devices. The
 extraordinary success of the IA32 architecture and the upcoming memory
-address space problem on IA32 high end servers, led to a special
+address space problem on IA32 high-end servers, led to a special
 design decision. Unlike Intel, with it's completely new designed IA64
 architecture, AMD decided to extend the IA32 instruction set with
 new 64-bit instructions.
 design decision. Unlike Intel, with it's completely new designed IA64
 architecture, AMD decided to extend the IA32 instruction set with
 new 64-bit instructions.
@@ -48,10 +48,10 @@ faster in 64-bit code.
 Another crucial pointer to make the AMD64 architecture faster than
 IA32, is the limited number of registers. Any IA32 architecture, from
 the early \textit{i386} to the newest generation of \textit{Intel
 Another crucial pointer to make the AMD64 architecture faster than
 IA32, is the limited number of registers. Any IA32 architecture, from
 the early \textit{i386} to the newest generation of \textit{Intel
-Pentium 4} or \textit{AMD Athlon}, has only 8 general purpose
+Pentium 4} or \textit{AMD Athlon}, has only 8 general-purpose
 registers. With the \textit{REX prefix}, AMD has the ability to
 increase the amount of accessible registers by 1 bit. This means in
 registers. With the \textit{REX prefix}, AMD has the ability to
 increase the amount of accessible registers by 1 bit. This means in
-\textit{64-bit Mode} 16 general purpose registers are available. The
+\textit{64-bit Mode} 16 general-purpose registers are available. The
 value of a \textit{REX prefix} is in the range \texttt{40h} through
 \texttt{4Fh}, depending on the particular bits used (see table
 \ref{REX}).
 value of a \textit{REX prefix} is in the range \texttt{40h} through
 \texttt{4Fh}, depending on the particular bits used (see table
 \ref{REX}).
@@ -86,28 +86,28 @@ instructions can handle both \textit{memory operands} and
 implementation of the IA32 ICMDs.
 
 Much better code generation can be achieved in the area of
 implementation of the IA32 ICMDs.
 
 Much better code generation can be achieved in the area of
-\textit{long arithmetic}. Since all 16 general purpose registers can
+\textit{long arithmetic}. Since all 16 general-purpose registers can
 hold 64-bit integer values, there is no need for special long
 handling, like on IA32 were we stored all long varibales in memory. A
 hold 64-bit integer values, there is no need for special long
 handling, like on IA32 were we stored all long varibales in memory. A
-simple \texttt{ICMD\_LADD} was on IA32 (best case shown for AMD64 ---
-\texttt{src->regoff == iptr->dst->regoff}):
+simple \texttt{ICMD\_LADD} on IA32, best case shown for AMD64 ---
+\texttt{src->regoff == iptr->dst->regoff}:
 
 \begin{verbatim}
 
 \begin{verbatim}
-i386_mov_membase_reg(REG_SP, src->prev->regoff * 8, REG_ITMP1);
-i386_alu_reg_membase(I386_ADD, REG_ITMP1, REG_SP, iptr->dst->regoff * 8);
-i386_mov_membase_reg(REG_SP, src->prev->regoff * 8 + 4, REG_ITMP1);
-i386_alu_reg_membase(I386_ADC, REG_ITMP1, REG_SP, iptr->dst->regoff * 8 + 4);
+        i386_mov_membase_reg(REG_SP, src->prev->regoff * 8, REG_ITMP1);
+        i386_alu_reg_membase(I386_ADD, REG_ITMP1, REG_SP, iptr->dst->regoff * 8);
+        i386_mov_membase_reg(REG_SP, src->prev->regoff * 8 + 4, REG_ITMP1);
+        i386_alu_reg_membase(I386_ADC, REG_ITMP1, REG_SP, iptr->dst->regoff * 8 + 4);
 \end{verbatim}
 
 First memory operand is added to second memory operand which is at the
 \end{verbatim}
 
 First memory operand is added to second memory operand which is at the
-same stack location as the destination operand. This are four
-instructions executed for one addition. If we would use registers for
-long variables we could get a \textit{best-case} of two instructions,
-namely \textit{add} followed by a \textit{adc}. On AMD64 we can
-generate one instruction for this addition:
+same stack location as the destination operand. This means, there are
+four instructions executed for one long addition. If we would use
+registers for long variables we could get a \textit{best-case} of two
+instructions, namely \textit{add} followed by an \textit{adc}. On
+AMD64 we can generate one instruction for this addition:
 
 \begin{verbatim}
 
 \begin{verbatim}
-x86_64_alu_reg_reg(X86_64_ADD, src->prev->regoff, iptr->dst->regoff);
+        x86_64_alu_reg_reg(X86_64_ADD, src->prev->regoff, iptr->dst->regoff);
 \end{verbatim}
 
 This means, the AMD64 port is \textit{four-times} faster than the IA32
 \end{verbatim}
 
 This means, the AMD64 port is \textit{four-times} faster than the IA32
@@ -121,8 +121,309 @@ mode without escaping --- with the mentioned \textit{REX prefix}
 byte. In CACAO we use a macro called
 
 \begin{verbatim}
 byte. In CACAO we use a macro called
 
 \begin{verbatim}
-x86_64_emit_rex(size,reg,index,rm)
+        x86_64_emit_rex(size,reg,index,rm)
 \end{verbatim}
 
 The names of the arguments are respective to their use in the
 \textit{REX prefix} (see table \ref{REX}).
 \end{verbatim}
 
 The names of the arguments are respective to their use in the
 \textit{REX prefix} (see table \ref{REX}).
+
+The AMD64 architecture introduces also a new addressing method called
+\textit{RIP-relative addressing}. In 64-bit mode, addressing relative
+to the contents of the 64-bit instruction pointer (program counter)
+--- called \textit{RIP-relative addressing} or \textit{PC-relative
+addressing} --- is implemented for certain instructions. In this
+instructions, the effective address is formed by adding the
+displacement to the 64-bit \texttt{RIP} of the next instruction. With
+this addressing mode, we can replace the IA32 method of addressing
+data in the method's data segment. Like in the
+\texttt{ICMD\_PUTSTATIC} instruction, the IA32 code
+
+\begin{verbatim}
+        a = dseg_addaddress(&(((fieldinfo *) iptr->val.a)->value));
+        i386_mov_imm_reg(0, REG_ITMP2);
+        dseg_adddata(mcodeptr);
+        i386_mov_membase_reg(REG_ITMP2, a, REG_ITMP2);
+\end{verbatim}
+
+can be replaced with the new \textit{RIP-relative addressing} code
+
+\begin{verbatim}
+        a = dseg_addaddress(&(((fieldinfo *) iptr->val.a)->value));
+        x86_64_mov_membase_reg(RIP, -(((s8) mcodeptr + 7) - (s8) mcodebase) + a, REG_ITMP2);
+\end{verbatim}
+
+So we can save one instruction on the read or write of an static
+variable. The additional offset of \texttt{+ 7} is the code size of
+the instruction itself. The fictive register \texttt{RIP} is defined
+with
+
+\begin{verbatim}
+        #define RIP    -1
+\end{verbatim}
+
+Thus we can determine the special \textit{RIP-relative addressing}
+mode in the code generating macro
+\texttt{x86\_64\_emit\_membase(basereg,disp,dreg)} with
+
+\begin{verbatim}
+        if ((basereg) == RIP) {
+            x86_64_address_byte(0,(dreg),RBP);
+            x86_64_emit_imm32((disp));
+            break;
+        }
+\end{verbatim}
+
+and generate the \textit{RIP-relative addressing} code. As shown in
+the code sample, it's an special encoding of the \textit{address byte}
+mit the \texttt{mod} field set to zero and \texttt{RBP}
+(\texttt{\%rbp}) as baseregister.
+
+
+\subsection{Constant handling}
+
+As on IA32, the AMD64 code generator can use \textit{immediate move}
+instructions to load integer constants into their destination
+registers. The 64-bit extensions of the AMD64 architecture can also
+load 64-bit immediates inline. So loading a \texttt{long} constant
+just uses one instruction, despite of two instructions on the IA32
+architecture. Of course the AMD64 code generator uses the \textit{move
+long} (\texttt{movl}) instruction to load 32-bit \texttt{int} constants
+to minimize code size. This instruction clears the upper 32-bit of the
+destination register.
+
+\begin{verbatim}
+        case ICMD_ICONST:
+                ...
+                x86_64_movl_imm_reg(cd, iptr->val.i, d);
+                ...
+
+        case ICMD_LCONST:
+                ...
+                x86_64_mov_imm_reg(cd, iptr->val.l, d);
+                ...
+\end{verbatim}
+
+\texttt{float} and \texttt{double} values are loaded from the data
+segment via the \textit{move doubleword or quadword} (\texttt{movd})
+instruction with \textit{RIP-relative addressing}.
+
+
+\subsection{Calling conventions}
+
+The AMD64 calling conventions are described here \ref{}. CACAO uses a
+subset of this calling convention, to cover its requirements. CACAO
+just needs to pass the JAVA data types, no other special features. The
+sizes of the JAVA data types on the AMD64 port are shown in table
+\ref{javadatatypesizes}.
+
+\begin{table}
+\begin{center}
+\begin{tabular}[b]{|l|c|}
+\hline
+JAVA Data Type   & Bytes \\ \hline
+\texttt{boolean} & 1     \\
+\texttt{byte}    &       \\
+\texttt{char}    &       \\ \hline
+\texttt{short}   & 2     \\ \hline
+\texttt{int}     & 4     \\
+\texttt{float}   &       \\ \hline
+\texttt{long}    & 8     \\
+\texttt{double}  &       \\
+\texttt{void}    &       \\ \hline
+\end{tabular}
+\caption{JAVA Data Type sizes on AMD64}
+\label{javadatatypesizes}
+\end{center}
+\end{table}
+
+\subsubsection{Integer arguments}
+
+The AMD64 architecture has 6 integer argument registers. The order of
+the argument registers is shown in table
+\ref{amd64integerargumentregisters}.
+
+\begin{table}
+\begin{center}
+\begin{tabular}[b]{|l|l|}
+\hline
+Register       & Argument Register \\ \hline
+\texttt{\%rdi} & 1$^{\rm st}$      \\ \hline
+\texttt{\%rsi} & 2$^{\rm nd}$      \\ \hline
+\texttt{\%rdx} & 3$^{\rm rd}$      \\ \hline
+\texttt{\%rcx} & 4$^{\rm th}$      \\ \hline
+\texttt{\%r8}  & 5$^{\rm th}$      \\ \hline
+\texttt{\%r9}  & 6$^{\rm th}$      \\ \hline
+\end{tabular}
+\caption{AMD64 Integer Argument Register}
+\label{amd64integerargumentregisters}
+\end{center}
+\end{table}
+
+As on RISC machines, the remaining integer arguments are passed on the
+stack. Each integer argument, regardless of which size, uses 8 bytes
+on the stack.
+
+Integer return values of any size are stored in \texttt{REG\_RESULT},
+which is \texttt{\%rax}.
+
+\subsubsection{Floating point arguments}
+
+The AMD64 architecture has 8 floating point argument registers, namely
+\texttt{\%xmm0} through \texttt{\%xmm7}. \texttt{\%xmm} registers are
+128-bit wide floating point registers on which SSE and SSE2
+instructions can operate. Remaining floating point arguments are
+passed, like with integer arguments, on the stack using 8 bytes per
+argument.
+
+Floating point return values are stored in \texttt{\%xmm0}.
+
+As shown, the calling conventions for the AMD64 architecture are
+nearly the same as for RISC machines, which allows to use CACAOs
+\textit{register allocator algorithm} and \textit{stack space
+allocation algorithm} without any changes.
+
+Calling native functions means register moves and stack copying like
+on RISC machines. This depends on the count of the arguments used for
+the called native function. For non-static native functions the first
+integer argument has to be the JNI environment variable, so any
+arguments passed need to be shifted by one register, which can include
+creating a new stackframe and storing some arguments on the
+stack. Additionally for static native functions the class pointer of
+the current objects' class is passed in the 2$^{\rm nd}$ integer
+argument register. This means that the integer argument registers need
+to be shifted by two registers.
+
+One difference of the calling convention to RISC type machines, like
+Alpha or MIPS, is the usage of integer and floating point argument
+registers with mixed integer and floating point arguments. Assume a
+function like this:
+
+\begin{verbatim}
+        void sub(int a, float b, long c, double d);
+\end{verbatim}
+
+On a RISC machine, like Alpha, we would have an argument register
+usage like in figure \ref{alphaargumentregisterusage}. \texttt{a?}
+represent integer argument registers and \texttt{fa?} floating point
+argument registers.
+
+\begin{figure}[htb]
+\begin{center}
+\setlength{\unitlength}{1mm}
+\begin{picture}(60,35)
+\thicklines
+\put(0,15){\framebox(15,10){a0 = a}}
+\put(30,15){\framebox(15,10){a2 = c}}
+\put(15,5){\framebox(15,10){fa1 = b}}
+\put(45,5){\framebox(15,10){fa3 = d}}
+\put(0,0){\line(0,1){30}}
+\end{picture}
+\caption{Alpha argument register usage for \texttt{void sub(int a, float b, long c, double d);}}
+\label{alphaargumentregisterusage}
+\end{center}
+\end{figure}
+
+On AMD64 the same function call would look like in figure
+\ref{amd64argumentregisterusage}.
+
+\begin{figure}[htb]
+\begin{center}
+\setlength{\unitlength}{1mm}
+\begin{picture}(60,35)
+\thicklines
+\put(0,15){\framebox(15,10){a0 = a}}
+\put(15,15){\framebox(15,10){a1 = c}}
+\put(0,5){\framebox(15,10){fa0 = b}}
+\put(15,5){\framebox(15,10){fa1 = d}}
+\put(0,0){\line(0,1){30}}
+\end{picture}
+\caption{AMD64 argument register usage for \texttt{void sub(int a, float b, long c, double d);}}
+\label{amd64argumentregisterusage}
+\end{center}
+\end{figure}
+
+The register assigment would be \texttt{a0 = \%rdi}, \texttt{a1 =
+\%rsi}, \texttt{fa0 = \%xmm0} and \texttt{fa1 = \%xmm1}. This special
+usage of the argument registers required some changes in the argument
+register allocation algorithm for function calls during stack
+analysis and some changes in the code generator itself.
+
+
+\subsection{Register allocator}
+
+As mentioned in the introduction, the AMD64 architecture has 16
+general-purpose registers and 16 floating-point registers. One
+general-purpose register is reserved for the \textit{stack pointer}
+--- namely \texttt{\%rsp} --- and thus cannot be used for arithmetic
+instructions. The register usage as used in CACAO is shown in table
+\ref{amd64registerusage}.
+
+\begin{table}
+\begin{center}
+\begin{tabular}{l|l|l}
+Register       & Usage                                        & Callee-saved \\ \hline
+\texttt{\%rax} & return register, reserved for code generator & no           \\
+\texttt{\%rcx} & 4$^{\rm th}$ argument register               & no           \\
+\texttt{\%rdx} & 3$^{\rm rd}$ argument register               & no           \\
+\texttt{\%rbx} & temporary register                           & no           \\
+\texttt{\%rsp} & stack pointer                                & yes          \\
+\texttt{\%rbp} & callee-saved register                        & yes          \\
+\texttt{\%rsi} & 2$^{\rm nd}$ argument register               & no           \\
+\texttt{\%rdi} & 1$^{\rm st}$ argument register               & no           \\
+\texttt{\%r8}  & 5$^{\rm th}$ argument register               & no           \\
+\texttt{\%r9}  & 6$^{\rm th}$ argument register               & no           \\
+\texttt{\%r10}-\texttt{\%r11} & reserved for code generator   & no           \\
+\texttt{\%r12}-\texttt{\%r15} & callee-saved register         & yes          \\
+\texttt{\%xmm0}-\texttt{\%xmm7} & argument registers          & no           \\
+\texttt{\%xmm8}-\texttt{\%xmm15} & temporary registers        & no           \\
+\end{tabular}
+\caption{AMD64 Register usage in CACAO}
+\label{amd64registerusage}
+\end{center}
+\end{table}
+
+There is only one change to the original AMD64 \textit{application
+binary interface} --- ABI. CACAO uses \texttt{\%rbx} as temporary
+register, while the AMD64 ABI uses the \texttt{\%rbx} register as
+callee-saved register.
+
+In adapting the register allocator there was a problem concerning the
+order of the integer argument registers. The order of the first four
+argument register is inverted. This fact can be seen in table
+\ref{amd64registerusage} which is ordered ascending by the processors'
+internal register numbers. That means the ascending search algorithm
+for argument registers in the register allocator would allocate the
+first four argument registers in the wrong direction. So there is a
+little hack implemented in CACAOs register allocator to handle this
+fact. After searching the register definition array for the argument
+registers, the first four argument registers are interchanged in their
+array. This is done by a simple code sequence (taken from
+\texttt{jit/reg.inc}):
+
+\begin{verbatim}
+        /* 
+         * on x86_64 the argument registers are not in ascending order 
+         * a00 (%rdi) <-> a03 (%rcx) and a01 (%rsi) <-> a02 (%rdx)
+         */
+        n = r->argintregs[3];
+        r->argintregs[3] = r->argintregs[0];
+        r->argintregs[0] = n;
+
+        n = r->argintregs[2];
+        r->argintregs[2] = r->argintregs[1];
+        r->argintregs[1] = n;
+\end{verbatim}
+
+
+\subsection{Floating point arithmetic}
+
+The AMD64 architecture has implemented two sets of floating point instructions:
+
+\begin{itemize}
+\item old i387 (x87)
+\item SSE and SSE2
+\end{itemize}
+
+The x87 \textit{floating point unit} (FPU) implementation is
+completely compatible to the IA32 implementation with all its
+advantages and drawbacks, like the FPU stack.