Bits
====
Arrays and Pointers
-------------------
This section is a short recap of some standard C functionality: data types, memory allocations, pointer arithmetic.
If these tasks look difficult to you:
* Try to get some additional practice outside of the class.
* Ask questions if you have any. No matter how "basic" they might be.
.. admonition:: Tasks
#. In all your tests below, compile in debug-mode, i.e., use ``-g -O0`` and run your program through `Valgrind `_.
#. Use the ``sizeof``-operator to determine the size of the data types ``unsigned char``, ``char``, ``unsigned int``, ``float``, ``double``.
What's the size of respective pointers, e.g., ``unsigned int *`` or ``float *``?
#. Use the functions ``malloc`` and ``free`` to allocate and free an array.
The array should be large enough to hold 1,500 values of type ``int``.
#. Initialize the array: :math:`a[i] = 3 \cdot i`.
#. Go through pointer arithmetic (do not use the ``[]``-operator) to print the value at position 500.
Print the memory addresses at position 250 and position 750, what's their distance in bytes?
Working with Bits
-----------------
We discussed data encodings in class.
These are key to understand how numerical, textual and program data is stored.
The used encoding can have a significant impact on the obtained performance.
For example, when microbenchmarking the sustainable peak performance on V1,
we observed a two-times higher floating point throughput when switching from double- to single-precision arithmetic.
In practice, we might have to manipulate raw data at the bit level.
C/C++ offers a series of operators and functions to do exactly this.
In this section we'll look at some low(er)-level C/C++ operators and functions.
.. admonition:: Tasks
#. Write C/C++ code to print the binary representations of the following variables.
Make sure that you understand why you obtain the respective results.
.. code-block:: cpp
unsigned char l_data1 = 1;
unsigned char l_data2 = 255;
unsigned char l_data3 = l_data2 + 1;
unsigned char l_data4 = 0xA1;
unsigned char l_data5 = 0b1001011;
unsigned char l_data6 = 'H';
char l_data7 = -4;
unsigned int l_data8 = 1u << 11;
unsigned int l_data9 = l_data8 << 21;
unsigned int l_data10 = 0xFFFFFFFF >> 5;
unsigned int l_data11 = 0b1001 ^ 0b01111;
unsigned int l_data12 = ~0b1001;
unsigned int l_data13 = 0xF0 & 0b1010101;
unsigned int l_data14 = 0b001 | 0b101;
unsigned int l_data15 = 7743;
int l_data16 = -7743;
#. Write the function ``instruction_asimd_compute`` which has the following signature:
.. code-block:: c++
unsigned int instruction_asimd_compute( unsigned int i_vec_instr,
unsigned char i_vec_reg_dst,
unsigned char i_vec_reg_src_0,
unsigned char i_vec_reg_src_1 );
It should manipulate ``i_vec_instr`` in the following way:
* Bits 0-4 should be set to bits 0-4 of ``i_vec_reg_dst``
* Bits 5-9 should be set to bits 0-4 of ``i_vec_reg_src_0``
* Bits 16-20 should be set to bits 0-4 of ``i_vec_reg_src_1``
The return value is the result of this procedure.
For example, assume that you get the following input:
* ``i_vec_instr = 0b01001110001000001100110000000000``
* ``i_vec_reg_dst = 0b00000000``
* ``i_vec_reg_src_0 = 0b00000001``
* ``i_vec_reg_src_1 = 0b00000010``
then the returned value should be ``0b01001110001000101100110000100000``.
#. Have a look at the documentation of `FMLA (vector) `_. What did we just accomplish through the example above?