2. Bits
2.1. 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.
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 typesunsigned char
,char
,unsigned int
,float
,double
. What’s the size of respective pointers, e.g.,unsigned int *
orfloat *
?Use the functions
malloc
andfree
to allocate and free an array. The array should be large enough to hold 1,500 values of typeint
.Initialize the array: \(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?
2.2. 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.
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.
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: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?