What are the Runtime Environment Limits in C?

Question: What are the Limits of One’s Runtime Environment

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:

advertisement
advertisement

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.

advertisement

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.

advertisement
If you wish to look at all C Tutorials, go to C Tutorials.

advertisement
advertisement
Subscribe to our Newsletters (Subject-wise). Participate in the Sanfoundry Certification contest to get free Certificate of Merit. Join our social networks below and stay updated with latest contests, videos, internships and jobs!

Youtube | Telegram | LinkedIn | Instagram | Facebook | Twitter | Pinterest
Manish Bhojasia - Founder & CTO at Sanfoundry
Manish Bhojasia, a technology veteran with 20+ years @ Cisco & Wipro, is Founder and CTO at Sanfoundry. He lives in Bangalore, and focuses on development of Linux Kernel, SAN Technologies, Advanced C, Data Structures & Alogrithms. Stay connected with him at LinkedIn.

Subscribe to his free Masterclasses at Youtube & discussions at Telegram SanfoundryClasses.