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 12typedef unsigned char U1; 13typedef unsigned short U2; 14typedef unsigned int U4; 15typedef unsigned long long U8; 16 17typedef float F4; 18typedef 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. 24U8 a [SZB_OF_a / 8]; // Type is U8 to ensure it's 8-aligned 25U8 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// 38U8 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. 68void 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 86int 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