/Teaching/System Level Programming/Assignments/A6
Pull from upstream before solving this task.
Task 6.1: Inline Assembly
Assembly Language
To be able to execute programs written in high level programming languages, they first have to be translated into CPU instructions
using a compiler. Language constructs and statements of high level programming languages are CPU architecture
independent and do not have any special relationship with individual CPU instructions
. The compiler is responsible for selecting and emitting the appropriate instructions which are required for the task at hand.
Assembly languages
are a special kind of low level programming languages. Unlike their high level counterparts, assembly languages are not architecture independent but instead target a specific instruction set. While language constructs in high level languages may be compiled into any number of instructions, each statement in an assembly language is translated into one specific CPU instruction
. An assembly language can also be thought of as a translation between human readable mnemonics
and binary opcodes
readable by the CPU (e.g.jmp = 0xE9
). A compiler for an assembly language is also called an assembler
.
While it is usually a lot more efficient and practical to write code in high level languages, some things still require low level assembly instructions because they are simply not possible to accomplish in other programming languages. This is especially true when using CPU hardware features
that can only be accessed using special instructions
.
When using the GCC
C compiler, it is possible to combine normal code written in C with assembly code in the same source file or even the same function by using GCC inline assembly
.
Your Task
Calculate the CPU frequency using inline assembly (5 Points)
Write a program that calculates the CPU clock frequency
by making use of special CPU instructions
via inline assembly
.
Use the rdtscp* instruction to fetch the current state of the CPU clock time stamp counter. By measuring the cycle count before and after an operation that takes a known amount of time (e.g. sleeping for a few microseconds using usleep( ... )
) and taking the difference, you should be able to calculate the clock frequency
. Convert your calculated frequency to GHz
and print it to stdout
. Take care to correctly use the clobber list to inform the compiler of any modified registers.
Use the provided framework to implement this assignment. Do not use any intrinsics or other functions to get the time stamp counter value, only use inline assembly.
* We use the rdtscp
instruction instead of the more common rdtsc
because of its serializing properties. Execution of the rdtsc
instruction can be shifted around internally by the CPU because of out-of-order execution and therefore provide inconsistent timing. The rdtscp
instruction (mostly) avoids this problem by preventing this internal re-ordering.
Task 6.2: ABI – Calling Conventions
Calling Conventions
Practically all programs are split into modular functions that an application may call from anywhere in the code. In order for this to work, the caller
of these functions as well as the called function (the callee
) need to have a set of rules that define, e.g. where the parameters for calling the function are stored (e.g., in registers
or on the stack
), where the return value
is stored and which registers the function may use without having to save the previous values. These rules are called Calling Conventions
and are part of the Application Binary Interface (ABI
). The ABI
is similar to an API
(Application Programming Interface) only on the instruction level and therefore heavily depends on the used architecture and compiler. On x86 64-bit
Linux, the standard calling convention is the System V AMD64
ABI. (32-bit Linux and Windows use different calling conventions.)
Your Tasks
Task A: Call a function in inline assembly (4 Points)
Familiarize yourself with the System V AMD64
64-bit calling convention and implement a function call
using only assembly instructions via gcc inline assembly
. Take care to avoid unintentionally interfering with code outside of your inline assembly block by correctly using the clobber list
to notify the compiler about potentially modified registers. (If you call a function in an inline assembly block, all side effects of that function call also need to be considered. Hint: see the calling convention for potential effects you need to take into account)
Use the provided framework in a_caller/caller.c
and see the comments for details on what you need to implement.
Do not use C code for this task, only inline assembly!
Task B: Implement a function in assembly (6 Points)
In this part, your task is to implement a small function in assembly in order to get to know the receiving end of a function call
.
Use x86 64-bit
assembly
to implement the following function in b_callee/sysv_abi.S
. Follow the System V AMD64
64-bit calling convention
like in the previous task and take care to e.g. save and restore registers as required.
size_t slp_memmultiply(char* dst, const char* src, unsigned int num_copies, size_t srclen) { size_t bytes_copied = 0; for(unsigned int copy_index = 0; copy_index < num_copies; ++copy_index) { for(size_t i = 0; i < srclen; ++i) { *dst = src[i]; ++dst; ++bytes_copied; } } return bytes_copied; }
Resources
Input/output constraints for gcc inline assembly:
- Generic (see ‘m’ and ‘r’ and ‘g’ constraints especially)
- Architecture specific (e.g. specific x86 registers)
Submission
Develop your solution in the A6 folder in your git repository and use the provided files. Changes to your Makefiles won’t be included on the test system. Do not change the given source files except for the part marked with TODO
.
Tag your submission with A6
and push it to the server. Your submission will be tested automatically.
Assignment Tutor
If you have any questions regarding this assignment, feel free to ask on Discord (or bs-helpline@iaik.tugraz.at as a second fallback option). If you have a more direct question regarding your specific solution, you can also ask the assignment tutor:
Florian Kargl, florian.kargl@student.tugraz.at