Answer: To begin with exploring one’s runtime environment, first step is to obtain assembly language listing for your compiler. On Linux system, ‘-S’ compiler option causes compiler to produce assembly code for your C program in a file that has ‘.s’ suffix. For example, say, ‘hello.c’ is C program, then command
# gcc -S hello.c
when executes produces ‘hello.s’ file containing assembly code for ‘hello.c’. Also, you need to interpret and understand assembly code, though it’s not required you must be an expert assembly language programmer. Just you should have basic understanding of what each instruction is doing and how to interpret addressing modes.
Firstly, let’s determine C declarations their corresponding Intel data types, x86-64 sizes and GCC assembler (GAS) suffixes in a table below:
C Declaration Intel data type GAS Suffix x86-64 Size (Bytes) char Byte b 1 short Word w 2 int Double word l 4 unsigned Double word l 4 long int Quad word q 8 unsigned long Quad word q 8 char * Quad word q 8 float Single precision s 4 double Double precision d 8 long double Extended precision t 16
Let’s put together important features below:
1. Notice that sizes of both long integers and pointers require 8 bytes, as compared to 4 for IA32.
2. Pointers and long integers are 64 bits long. Integer arithmetic operations support 8, 16, 32, and 64-bit data types.
3. The set of general-purpose registers is expanded from 8 to 16. The new registers are %r8 through %r15.
4. Much of the program state is held in registers rather than on the stack. Integer and pointer procedure arguments (up to 6) are passed via registers. Some procedures do not need to access the stack at all.
5. Conditional operations are implemented using conditional move instructions when possible, yielding better performance than traditional branching code.
6. Floating-point operations are implemented using a register-oriented instruction set, rather than the the stack-based support provided by IA32.
Arguments to procedures via registers:
With x86-64, we can pass up to max 6 arguments viz. integers and pointers, to procedures via registers, let’s consider a simple C program with more than 6 arguments being passed to procedure ‘max_reg_parameters()’
void max_reg_parameters(int i, unsigned int ui, long int li, unsigned long ul, char * p2c, long int *p2li, long double *p2ld) { /* manipulations here*/ }
x86-64 implementation of the above C code fragment
1. max_reg_parameters: 2. .cfi_startproc function begins here 3. pushq %rbp callee save 4. movq %rsp, %rbp stack-pointer copied to reg 'rbp' 5. movl %edi, -4(%rbp) int i in reg '%edi' 6. movl %esi, -8(%rbp) unsigned int ui in reg '%esi' 7. movq %rdx, -16(%rbp) long int li in reg '%rdx' 8. movq %rcx, -24(%rbp) unsigned long ul in reg '%rcx' 9. movq %r8, -32(%rbp) p2c in reg '%r8' 10. movq %r9, -40(%rbp) p2li in reg '%r9' 11. popq %rbp restore callee 12. ret function returns 13. .cfi_endproc function ends here
Notice that first six of seven arguments passed to procedure ‘max_reg_parameters()’ were stored in registers. Also notice that int ‘i’ and unsigned int ‘ui’ are stored in 32-bit sub-sections, ‘%edi’ and ‘%esi’ of registers ‘%rdi’ and ‘%rsi’ respectively. long int ‘li’, unsigned long ‘ul’, pointer-to-char ‘p2c’ and pointer-to-long-int ‘p2li’, each is 64-bit in size and are allocated in registers, in sequence, ‘%rdx’, ‘%rcx’, ‘%r8’ and ‘%r9’.
Notice also a very interesting feature of this implementation that there wasn’t created any stack frame. x86-64 ISA allows any program to access up to 128 bytes beyond (lower addresses to) the current value of ‘%rsp’ (stack pointer). Allocation of memory in this region is done by Virtual Memory Management System and this region is called ‘red zone’. Advantage of this technique is to avoid any overheads incurred in allocation and deallocation of stack frame and therefore there’s no need for frame pointer and values on stack can be accessed simply with relative to stack-pointer. Moreover, if arguments being passed to a procedure are less than six, then procedure might not require stack at all.
General Purpose Registers of x86-64 ISA
63 31 15 8 7 0 %rax %eax %ax %ah %al Return value %rbx %ebx %ax %bh %bl Callee saved %rcx %ecx %cx %ch %cl 4th argument %rdx %edx %dx %dh %dl 3rd argument %rsi %esi %si %sil 2nd argument %rdi %edi %di %dil 1st argument %rbp %ebp %bp %bpl Callee saved %rsp %esp %sp %spl Stack pointer %r8 %r8d %r8w %r8b 5th argument %r9 %r9d %r9w %r9b 6th argument %r10 %r10d %r10w %r10b Callee saved %r11 %r11d %r11w %r11b Used for linking %r12 %r12d %r12w %r12b Unused for C %r13 %r13d %r13w %r13b Callee saved %r14 %r14d %r14w %r14b Callee saved %r15 %r15d %r15w %r15b Callee saved
Notice that existing eight registers are extended to 64-bit versions, and eight new registers are added. Each register can be accessed as either 8 bits (byte), 16 bits (word), 32 bits (double word), or 64 bits (quad word).
Sanfoundry Global Education & Learning Series – 1000 C Tutorials.
- Practice BCA MCQs
- Practice Computer Science MCQs
- Apply for C Internship
- Check Computer Science Books
- Check C Books