File: sh-mem.c

package info (click to toggle)
valgrind 1%3A3.24.0-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 176,332 kB
  • sloc: ansic: 795,029; exp: 26,134; xml: 23,472; asm: 14,393; cpp: 9,397; makefile: 7,464; sh: 6,122; perl: 5,446; python: 1,498; javascript: 981; awk: 166; csh: 1
file content (218 lines) | stat: -rw-r--r-- 8,256 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// This program is a thorough test of the LOADVn/STOREVn shadow memory
// operations.

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "memcheck/memcheck.h"

// All the sizes here are in *bytes*, not bits.

typedef unsigned char        U1;
typedef unsigned short       U2;
typedef unsigned int         U4;
typedef unsigned long long   U8;

typedef float                F4;
typedef double               F8;

#define SZB_OF_a    64

// a[] is the array in which we do our loads and stores.  
// b[] is another one in which we do some copying.
U8 a [SZB_OF_a / 8];    // Type is U8 to ensure it's 8-aligned
U8 b [SZB_OF_a / 8];    // same size as a[]

// XXX: should check the error cases for SET/GET_VBITS also

// For the byte 'x', build a value of 'size' bytes from that byte, eg:
//   size 1 --> x
//   size 2 --> xx
//   size 4 --> xxxx
//   size 8 --> xxxxxxxx
// where the 0 bits are seen by Memcheck as defined, and the 1 bits are
// seen as undefined (ie. the value of each bit matches its V bit, ie. the
// resulting value is the same as its metavalue).
//
U8 build(int size, U1 byte)
{
   int i;
   U8 mask = 0;
   U8 shres;
   U8 res = 0xffffffffffffffffULL, res2;
   (void)VALGRIND_MAKE_MEM_UNDEFINED(&res, 8);
   assert(1 == size || 2 == size || 4 == size || 8 == size);

   for (i = 0; i < size; i++) {
      mask <<= 8;
      mask |= (U8)byte;
   }

   res &= mask;      
   
   // res is now considered partially defined, but we know exactly what its
   // value is (it happens to be the same as its metavalue).
   
   (void)VALGRIND_GET_VBITS(&res, &shres, 8);
   res2 = res;
   (void)VALGRIND_MAKE_MEM_DEFINED(&res2, 8);  // avoid the 'undefined' warning
   assert(res2 == shres);
   return res;
}

// Check that all the bytes in a[x..y-1] have their V byte equal 
// to either 'expected_byte' or 'expected_byte_alt'.
// 'str' and 'offset' are only used for printing an error message if
// something goes wrong.
void check_all(U4 x, U4 y, U1 expected_byte, U1 expected_byte_alt, 
                           char* str, int offset)
{
   U1 sh[SZB_OF_a];     // Used for getting a[]'s V bits
   int i;

   (void)VALGRIND_GET_VBITS(a, sh, sizeof(a));
   for (i = x; i < y; i++) {
      if ( expected_byte != sh[i] && expected_byte_alt != sh[i] ) {
         fprintf(stderr, "\n\nFAILURE: %s, offset %d, byte %d -- "
                         "is 0x%x, should be 0x%x or 0x%x\n\n",
                         str, offset, i, sh[i], expected_byte, 
                         expected_byte_alt);
         exit(1);
      }
   }
}

int main(void)
{
   int h, i, j;
   U1 *undefA, expected_byte, expected_byte_alt;

   if (0 == RUNNING_ON_VALGRIND) {
      fprintf(stderr,
              "error: this program only works when run under Valgrind\n");
      exit(1);
   }

   // Check a[] has the expected alignment, and that it's not too high in
   // the address space (which would trigger the slow cases in
   // LOADVn/STOREVn) on 64-bit platforms).
   assert( 0 == (long)a % 8);
   if (sizeof(void*) == 8) {
      assert( ((U1*)(&a[0])) < ((U1*)(32ULL * 1024*1024*1024)/*32G*/) );
   }

   // Check basic types have the expected sizes.
   assert(1 == sizeof(U1));
   assert(2 == sizeof(U2));
   assert(4 == sizeof(U4));
   assert(8 == sizeof(U8));

   // Create an array of values that has all the possible V bit metavalues.
   // Because 0 represents a defined bit, and because undefA[] is initially
   // zeroed, we have the nice property that:
   //
   //    i == undefA[i] == V_bits_of(undefA[i])
   //
   // which is useful for testing below.
   undefA = calloc(1, 256);         // one for each possible undefinedness value
   (void)VALGRIND_MAKE_MEM_UNDEFINED(undefA, 256);
   for (i = 0; i < 256; i++) {
      undefA[i] &= i; 
   }

   // This code does a whole lot of reads and writes of a particular size
   // (NNN = 1, 2, 4 or 8), with varying alignments, of values with
   // different not/partially/fully defined metavalues, and checks that the
   // V bits are set in a[] as expected using GET_VBITS.
   //
   // 'Ty' is the type of the thing we are copying.  It can be an integer
   // type or an FP type.  'ITy' is the same-sized integer type (and thus
   // will be the same as 'Ty' if 'ITy' is an integer type).  'ITy' is used
   // when doing shifting/masking and stuff like that.

#define DO(NNN, Ty, ITy, isF4) \
   fprintf(stderr, "-- NNN: %d %s %s ------------------------\n", \
           NNN, #Ty, #ITy); \
   /* For all of the alignments from (0..NNN-1), eg. if NNN==4, we do */ \
   /* alignments of 0, 1, 2, 3. */ \
   for (h = 0; h < NNN; h++) { \
      \
      size_t n  = sizeof(a); \
      size_t nN = n / sizeof(Ty); \
      Ty* aN    = (Ty*)a; \
      Ty* bN    = (Ty*)b; \
      Ty* aNb   = (Ty*)(((U1*)aN) + h); /* set offset from a[] */ \
      Ty* bNb   = (Ty*)(((U1*)bN) + h); /* set offset from b[] */ \
      \
      fprintf(stderr, "h = %d (checking %d..%d)   ", h, h, (int)(n-NNN+h)); \
      \
      /* For each of the 256 possible V byte values... */ \
      for (j = 0; j < 256; j++) { \
         /* build the value for i (one of: i, ii, iiii, iiiiiiii) */ \
         U8  tmp        = build(NNN, j); \
         ITy undefN_ITy = (ITy)tmp; \
         Ty* undefN_Ty; \
         { /* This just checks that no overflow occurred when squeezing */ \
           /* the output of build() into a variable of type 'Ty'. */ \
            U8  tmpDef     = tmp; \
            ITy undefN_ITyDef = undefN_ITy; \
            (void)VALGRIND_MAKE_MEM_DEFINED(&tmpDef,        8  );       \
            (void)VALGRIND_MAKE_MEM_DEFINED(&undefN_ITyDef, NNN);       \
            assert(tmpDef == (U8)undefN_ITyDef); \
         } \
         \
         /* We have to use an array for undefN_Ty -- because if we try to
          * convert an integer type from build into an FP type with a
          * straight cast -- eg "float f = (float)i" -- the value gets
          * converted.  With this pointer/array nonsense the exact bit
          * pattern gets used as an FP value unchanged (that FP value is
          * undoubtedly nonsense, but that's not a problem here). */ \
         undefN_Ty = (Ty*)&undefN_ITy; \
         if (0 == j % 32) fprintf(stderr, "%d...", j); /* progress meter */ \
         \
         /* A nasty exception: most machines so far (x86/PPC32/PPC64)
          * don't have 32-bit floats.  So 32-bit floats get cast to 64-bit
          * floats.  Memcheck does a PCast in this case, which means that if
          * any V bits for the 32-bit float are undefined (ie. 0 != j), all
          * the V bits in the 64-bit float are undefined.  So account for
          * this when checking.  AMD64 typically does FP arithmetic on
          * SSE, effectively giving it access to 32-bit FP registers.  So
          * in short, for floats, we have to allow either 'j' or 0xFF
          * as an acceptable result.  Sigh. */ \
         if (isF4) { \
            expected_byte = j; \
            expected_byte_alt = 0 != j ? 0xFF : j; \
         } else { \
            expected_byte = j; \
            expected_byte_alt = j; \
         } \
         \
         /* STOREVn.  Note that we use the first element of the undefN_Ty
          * array, as explained above. */ \
         for (i = 0; i < nN-1; i++) { aNb[i] = undefN_Ty[0]; } \
         check_all(h, n-NNN+h, expected_byte, expected_byte_alt, \
                   "STOREVn", h); \
         \
         /* LOADVn -- by copying the values to one place and then back, 
          * we ensure that LOADVn gets exercised. */ \
         for (i = 0; i < nN-1; i++) { bNb[i] = aNb[i]; } \
         for (i = 0; i < nN-1; i++) { aNb[i] = bNb[i]; } \
         check_all(h, n-NNN+h, expected_byte, expected_byte_alt, "LOADVn", h); \
      } \
      fprintf(stderr, "\n"); \
   }

   // For sizes 4 and 8 we do both integer and floating-point types.  The
   // reason being that on 32-bit machines just using integer types never
   // exercises LOADV8/STOREV8 -- for integer types these loads/stores get
   // broken into two 32-bit loads/stores.
   DO(1, U1, U1, /*isF4*/0);
   DO(2, U2, U2, /*isF4*/0);
   DO(4, U4, U4, /*isF4*/0);
   DO(4, F4, U4, /*isF4*/1);
   DO(8, U8, U8, /*isF4*/0);
   DO(8, F8, U8, /*isF4*/0);
   
   return 0;
}