Home | History | Annotate | Download | only in tests
      1 // This program is a thorough test of the LOADVn/STOREVn shadow memory
      2 // operations.
      3 
      4 #include <assert.h>
      5 #include <stdlib.h>
      6 #include <stdio.h>
      7 #include <string.h>
      8 #include "memcheck/memcheck.h"
      9 
     10 // All the sizes here are in *bytes*, not bits.
     11 
     12 typedef unsigned char        U1;
     13 typedef unsigned short       U2;
     14 typedef unsigned int         U4;
     15 typedef unsigned long long   U8;
     16 
     17 typedef float                F4;
     18 typedef double               F8;
     19 
     20 #define SZB_OF_a    64
     21 
     22 // a[] is the array in which we do our loads and stores.
     23 // b[] is another one in which we do some copying.
     24 U8 a [SZB_OF_a / 8];    // Type is U8 to ensure it's 8-aligned
     25 U8 b [SZB_OF_a / 8];    // same size as a[]
     26 
     27 // XXX: should check the error cases for SET/GET_VBITS also
     28 
     29 // For the byte 'x', build a value of 'size' bytes from that byte, eg:
     30 //   size 1 --> x
     31 //   size 2 --> xx
     32 //   size 4 --> xxxx
     33 //   size 8 --> xxxxxxxx
     34 // where the 0 bits are seen by Memcheck as defined, and the 1 bits are
     35 // seen as undefined (ie. the value of each bit matches its V bit, ie. the
     36 // resulting value is the same as its metavalue).
     37 //
     38 U8 build(int size, U1 byte)
     39 {
     40    int i;
     41    U8 mask = 0;
     42    U8 shres;
     43    U8 res = 0xffffffffffffffffULL, res2;
     44    (void)VALGRIND_MAKE_MEM_UNDEFINED(&res, 8);
     45    assert(1 == size || 2 == size || 4 == size || 8 == size);
     46 
     47    for (i = 0; i < size; i++) {
     48       mask <<= 8;
     49       mask |= (U8)byte;
     50    }
     51 
     52    res &= mask;
     53 
     54    // res is now considered partially defined, but we know exactly what its
     55    // value is (it happens to be the same as its metavalue).
     56 
     57    (void)VALGRIND_GET_VBITS(&res, &shres, 8);
     58    res2 = res;
     59    (void)VALGRIND_MAKE_MEM_DEFINED(&res2, 8);  // avoid the 'undefined' warning
     60    assert(res2 == shres);
     61    return res;
     62 }
     63 
     64 // Check that all the bytes in a[x..y-1] have their V byte equal
     65 // to either 'expected_byte' or 'expected_byte_alt'.
     66 // 'str' and 'offset' are only used for printing an error message if
     67 // something goes wrong.
     68 void check_all(U4 x, U4 y, U1 expected_byte, U1 expected_byte_alt,
     69                            char* str, int offset)
     70 {
     71    U1 sh[SZB_OF_a];     // Used for getting a[]'s V bits
     72    int i;
     73 
     74    (void)VALGRIND_GET_VBITS(a, sh, sizeof(a));
     75    for (i = x; i < y; i++) {
     76       if ( expected_byte != sh[i] && expected_byte_alt != sh[i] ) {
     77          fprintf(stderr, "\n\nFAILURE: %s, offset %d, byte %d -- "
     78                          "is 0x%x, should be 0x%x or 0x%x\n\n",
     79                          str, offset, i, sh[i], expected_byte,
     80                          expected_byte_alt);
     81          exit(1);
     82       }
     83    }
     84 }
     85 
     86 int main(void)
     87 {
     88    int h, i, j;
     89    U1 *undefA, expected_byte, expected_byte_alt;
     90 
     91    if (0 == RUNNING_ON_VALGRIND) {
     92       fprintf(stderr,
     93               "error: this program only works when run under Valgrind\n");
     94       exit(1);
     95    }
     96 
     97    // Check a[] has the expected alignment, and that it's not too high in
     98    // the address space (which would trigger the slow cases in
     99    // LOADVn/STOREVn) on 64-bit platforms).
    100    assert( 0 == (long)a % 8);
    101    if (sizeof(void*) == 8) {
    102       assert( ((U1*)(&a[0])) < ((U1*)(32ULL * 1024*1024*1024)/*32G*/) );
    103    }
    104 
    105    // Check basic types have the expected sizes.
    106    assert(1 == sizeof(U1));
    107    assert(2 == sizeof(U2));
    108    assert(4 == sizeof(U4));
    109    assert(8 == sizeof(U8));
    110 
    111    // Create an array of values that has all the possible V bit metavalues.
    112    // Because 0 represents a defined bit, and because undefA[] is initially
    113    // zeroed, we have the nice property that:
    114    //
    115    //    i == undefA[i] == V_bits_of(undefA[i])
    116    //
    117    // which is useful for testing below.
    118    undefA = calloc(1, 256);         // one for each possible undefinedness value
    119    (void)VALGRIND_MAKE_MEM_UNDEFINED(undefA, 256);
    120    for (i = 0; i < 256; i++) {
    121       undefA[i] &= i;
    122    }
    123 
    124    // This code does a whole lot of reads and writes of a particular size
    125    // (NNN = 1, 2, 4 or 8), with varying alignments, of values with
    126    // different not/partially/fully defined metavalues, and checks that the
    127    // V bits are set in a[] as expected using GET_VBITS.
    128    //
    129    // 'Ty' is the type of the thing we are copying.  It can be an integer
    130    // type or an FP type.  'ITy' is the same-sized integer type (and thus
    131    // will be the same as 'Ty' if 'ITy' is an integer type).  'ITy' is used
    132    // when doing shifting/masking and stuff like that.
    133 
    134 #define DO(NNN, Ty, ITy, isF4) \
    135    fprintf(stderr, "-- NNN: %d %s %s ------------------------\n", \
    136            NNN, #Ty, #ITy); \
    137    /* For all of the alignments from (0..NNN-1), eg. if NNN==4, we do */ \
    138    /* alignments of 0, 1, 2, 3. */ \
    139    for (h = 0; h < NNN; h++) { \
    140       \
    141       size_t n  = sizeof(a); \
    142       size_t nN = n / sizeof(Ty); \
    143       Ty* aN    = (Ty*)a; \
    144       Ty* bN    = (Ty*)b; \
    145       Ty* aNb   = (Ty*)(((U1*)aN) + h); /* set offset from a[] */ \
    146       Ty* bNb   = (Ty*)(((U1*)bN) + h); /* set offset from b[] */ \
    147       \
    148       fprintf(stderr, "h = %d (checking %d..%d)   ", h, h, (int)(n-NNN+h)); \
    149       \
    150       /* For each of the 256 possible V byte values... */ \
    151       for (j = 0; j < 256; j++) { \
    152          /* build the value for i (one of: i, ii, iiii, iiiiiiii) */ \
    153          U8  tmp        = build(NNN, j); \
    154          ITy undefN_ITy = (ITy)tmp; \
    155          Ty* undefN_Ty; \
    156          { /* This just checks that no overflow occurred when squeezing */ \
    157            /* the output of build() into a variable of type 'Ty'. */ \
    158             U8  tmpDef     = tmp; \
    159             ITy undefN_ITyDef = undefN_ITy; \
    160             (void)VALGRIND_MAKE_MEM_DEFINED(&tmpDef,        8  );       \
    161             (void)VALGRIND_MAKE_MEM_DEFINED(&undefN_ITyDef, NNN);       \
    162             assert(tmpDef == (U8)undefN_ITyDef); \
    163          } \
    164          \
    165          /* We have to use an array for undefN_Ty -- because if we try to
    166           * convert an integer type from build into an FP type with a
    167           * straight cast -- eg "float f = (float)i" -- the value gets
    168           * converted.  With this pointer/array nonsense the exact bit
    169           * pattern gets used as an FP value unchanged (that FP value is
    170           * undoubtedly nonsense, but that's not a problem here). */ \
    171          undefN_Ty = (Ty*)&undefN_ITy; \
    172          if (0 == j % 32) fprintf(stderr, "%d...", j); /* progress meter */ \
    173          \
    174          /* A nasty exception: most machines so far (x86/PPC32/PPC64)
    175           * don't have 32-bit floats.  So 32-bit floats get cast to 64-bit
    176           * floats.  Memcheck does a PCast in this case, which means that if
    177           * any V bits for the 32-bit float are undefined (ie. 0 != j), all
    178           * the V bits in the 64-bit float are undefined.  So account for
    179           * this when checking.  AMD64 typically does FP arithmetic on
    180           * SSE, effectively giving it access to 32-bit FP registers.  So
    181           * in short, for floats, we have to allow either 'j' or 0xFF
    182           * as an acceptable result.  Sigh. */ \
    183          if (isF4) { \
    184             expected_byte = j; \
    185             expected_byte_alt = 0 != j ? 0xFF : j; \
    186          } else { \
    187             expected_byte = j; \
    188             expected_byte_alt = j; \
    189          } \
    190          \
    191          /* STOREVn.  Note that we use the first element of the undefN_Ty
    192           * array, as explained above. */ \
    193          for (i = 0; i < nN-1; i++) { aNb[i] = undefN_Ty[0]; } \
    194          check_all(h, n-NNN+h, expected_byte, expected_byte_alt, \
    195                    "STOREVn", h); \
    196          \
    197          /* LOADVn -- by copying the values to one place and then back,
    198           * we ensure that LOADVn gets exercised. */ \
    199          for (i = 0; i < nN-1; i++) { bNb[i] = aNb[i]; } \
    200          for (i = 0; i < nN-1; i++) { aNb[i] = bNb[i]; } \
    201          check_all(h, n-NNN+h, expected_byte, expected_byte_alt, "LOADVn", h); \
    202       } \
    203       fprintf(stderr, "\n"); \
    204    }
    205 
    206    // For sizes 4 and 8 we do both integer and floating-point types.  The
    207    // reason being that on 32-bit machines just using integer types never
    208    // exercises LOADV8/STOREV8 -- for integer types these loads/stores get
    209    // broken into two 32-bit loads/stores.
    210    DO(1, U1, U1, /*isF4*/0);
    211    DO(2, U2, U2, /*isF4*/0);
    212    DO(4, U4, U4, /*isF4*/0);
    213    DO(4, F4, U4, /*isF4*/1);
    214    DO(8, U8, U8, /*isF4*/0);
    215    DO(8, F8, U8, /*isF4*/0);
    216 
    217    return 0;
    218 }
    219