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?