The following is
x86-64 machine code for an algorithm to calculate the
nth
Fibonacci number, with values in
hexadecimal representation and each line corresponding to one instruction: 89 f8 85 ff 74 26 83 ff 02 76 1c 89 f9 ba 01 00 00 00 be 01 00 00 00 8d 04 16 83 f9 02 74 0d 89 d6 ff c9 89 c2 eb f0 b8 01 00 00 c3 The following is the same algorithm written in
x86-64 assembly language using
Intel syntax. The
registers of the x86-64 processor are named and manipulated directly. The function loads its 64-bit argument from in accordance to the
System V application binary interface for x86-64 and performs its calculation by manipulating values in the , , , and registers until it has finished and returns. Note that in this assembly language, there is no concept of returning a value. The result having been stored in the register, again in accordance with System V application binary interface, the instruction simply removes the top 64-bit element on the
stack and causes the next instruction to be fetched from that location (that instruction is usually the instruction immediately after the one that called this function), with the result of the function being stored in . x86-64 assembly language imposes no standard for passing values to a function or returning values from a function (and in fact, has no concept of a function); those are defined by an
application binary interface (ABI), such as the System V ABI for a particular instruction set. fib: mov rax, rdi ; The argument is stored in rdi, put it into rax test rdi, rdi ; Is the argument zero? je .return_from_fib ; Yes - return 0, which is already in rax cmp rdi, 2 ; No - compare the argument to 2 jbe .return_1_from_fib ; If it is less than or equal to 2, return 1 mov rcx, rdi ; Otherwise, put it in rcx, for use as a counter mov rdx, 1 ; The first previous number starts out as 1, put it in rdx mov rsi, 1 ; The second previous number also starts out as 1, put it in rsi .fib_loop: lea rax, [rsi + rdx] ; Put the sum of the previous two numbers into rax cmp rcx, 2 ; Is the counter 2? je .return_from_fib ; Yes - rax contains the result mov rsi, rdx ; No - make the first previous number the second previous number dec rcx ; Decrement the counter mov rdx, rax ; Make the current number the first previous number jmp .fib_loop ; Keep going .return_1_from_fib: mov rax, 1 ; Set the return value to 1 .return_from_fib: ret ; Return The following is the same algorithm again, but in C. This is similar in structure to the assembly example but there are significant differences in abstraction: • The input (parameter ) is an abstraction that does not specify any storage location on the hardware. In practice, the C compiler follows one of many possible
calling conventions to determine a storage location for the input. • The local variables , , and are abstractions that do not specify any specific storage location on the hardware. The C compiler decides how to actually store them for the target architecture. • The return function specifies the value to return, but does not dictate
how it is returned. The C compiler for any specific architecture implements a
standard mechanism for returning the value. Compilers for the x86-64 architecture typically (but not always) use the register to return a value, as in the assembly language example (the author of the assembly language example has
chosen to use the System V application binary interface for x86-64 convention but assembly language does not require this). These abstractions make the C code compilable without modification for any architecture that is supported by a C compiler; whereas the assembly code above only runs on processors using the x86-64 architecture. unsigned int fib(unsigned int n) { if (!n) { return 0; } else if (n ==Low-level programming in high-level languages==