source: tests/array-collections/boxed.main.cfa@ fb0f04d

Last change on this file since fb0f04d was fd4df379, checked in by Michael Brooks <mlbrooks@…>, 14 months ago

Implement boxing for arrays.

The added test is things that did not work before.

  • Property mode set to 100644
File size: 21.6 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2023 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// boxed.main.cfa -- core logic of the "array boxed" test
8//
9// Author : Mike Brooks
10// Created On : Thu Jul 25 17:00:00 2024
11// Last Modified By :
12// Last Modified On :
13// Update Count :
14//
15
16// See abbreviation definitions in boxed.cases.hfa.
17
18/*
19The "array boxed" test deals with an array of T's, when T is dynamically sized.
20
21All cases generate a VLA, because even a sinlge (dynamically sized) T would be
22backed by a VLA. All cases generate pointer arithmetic on, and casts from,
23void*, because (dynamically sized) T has no correspondig type in generated C.
24These facts are true about boxing in general. The test ensures that the VLA
25is big enough and that accessed elements are spaced by the correct amounts,
26specifically for cases where the user declares an array of T's, i.e. demands
27several adjacent char-buffer-implemented T's.
28
29The core test logic occurs in the functions named allocAndAccess, below. It
30allocates an array of T's, then accesses them. In some cases, the access is
31within the allocAndAccess function, in others, it's within a called helper
32function. The access logic prints information about the spacing of the
33elements (as it sees them) and it stores the array-edge addreses for
34subsequent validation.
35
36The access output uses n, rather than (n-1), as its "end" address, just to
37keep expectation arithmetic simple. So the output does discuss addresses of
38elements that do not exist.
39
40The access output uses an expectedElemSz parameter, and calculations from it.
41Care is taken to ensure that we are not merely comparing two executions of the
42same, possibly flawed, math. First, the value of expectedElemSz is always
43calculated using concrete types, e.g. sizeof(float), while the SUT-produced
44value is from (implied use of) literally sizeof(T), just in a case where we
45have T=float. Second, the details within the calculation are not the main
46feature of interest, rather, it's _whether_ this calcuation is being applied
47in the cases where it should be, instead of, for example, seeming to assume
48sizeof(T)==1 or sizeof(T)==sizeof(size_t), both being bugs that actually
49occurred.
50
51An allocAndAccess function runs in an instrumentation context that observes
52the stack frame that allocAndAccess gets. This instrumentation verifies that
53the recorded array-edge addresses are within the stack frame. If the SUT has
54a bug due to a mistake in the box-pass's generated buffer declaration causes
55a function (like allocAndAccess) that declares an array of T's to get an
56incorrectly sized stack frame. This test was created along with a fix of such
57a bug.
58
59Including the instrumentation context, the call graph is:
60 main
61 run_X
62 bookendOuter_X
63 allocAndAccess_X
64 bookendInner
65 reportBookends
66The outer and inner "bookend" functions record the addresses of a local
67variable within their respective stack frames, thus giving a lenient
68approximation of the extent of the allocAndAccess stack frame, and
69thereby, of its VLA. Requiring a sufficiently large VLA, and seeing the
70resulting access stay in bounds (with constant overhead shown under verbose
71output) gives confidence in the actual VLA being of the right size.
72
73For this instrumentation to work, separate compilation (optimization) units
74are required: outer and inner "bookend" functions in one, allocAndAccess in the
75other. Otherwise, the optimizer sees the full call chain and compresses its
76use of frame pointers / VLA zones, into one ABI frame. Then, the outer and
77inner reference local varaibles no longer span the VLA. So, the "bookend"
78routines are in boxed.bookend.cfa, while everything else is here.
79
80These code elements are boilerplate, and are realized with macros driven by the
81tables in boxed.cases.hfa:
82 boxed.main.cfa main calls run_X
83 boxed.main.cfa declaration and definition of run_X, including
84 calling bookendOuter_X
85 calling reportBookends
86 boxed.hfa declaration of bookendOuter_X
87 boxed.bookend.cfa definition of bookendOuter_X, including
88 calling allocAndAccess_X
89 boxed.hfa declaration of allocAndAccess_X
90The definition of allocAndAcces_X is kept bespoke, to keep the actual test
91details readable. As a result, the list of allocAndAccess_X definition in
92boxed.main.cfa must be kept aligned with the tables in boxed.cases.hfa.
93A common definition of bookendInner is used acress all test cases, so its
94declaration and definition are not table driven.
95
96*/
97
98#include "boxed.hfa"
99
100#define SHOW_ACCESS_1D( N_ELEMS ) \
101 char * e0 = (char *) & x[0]; \
102 char * e1 = (char *) & x[1]; \
103 char * e2 = (char *) & x[2]; \
104 char * en = (char *) & x[N_ELEMS]; \
105 \
106 ptrdiff_t d01 = e1 - e0; \
107 ptrdiff_t d12 = e2 - e1; \
108 ptrdiff_t d02 = e2 - e0; \
109 ptrdiff_t d0n = en - e0; \
110 \
111 printf("Delta 0--1 expected %zd bytes, actual %zd bytes\n", 1 * expectedElmSz, d01); \
112 printf("Delta 1--2 expected %zd bytes, actual %zd bytes\n", 1 * expectedElmSz, d12); \
113 printf("Delta 0--2 expected %zd bytes, actual %zd bytes\n", 2 * expectedElmSz, d02); \
114 printf("Delta 0--n expected %zd bytes, actual %zd bytes\n", N_ELEMS * expectedElmSz, d0n); \
115 \
116 VPRT( "Array start %p end %p\n", e0, en ); \
117 \
118 ar_lo = e0; \
119 ar_hi = en;
120
121
122#define SHOW_ACCESS_2D( N_ELEMS ) \
123 char * e00 = (char *) & x[0][0]; \
124 char * e01 = (char *) & x[0][1]; \
125 char * e02 = (char *) & x[0][2]; \
126 char * e0n = (char *) & x[0][N_ELEMS]; \
127 \
128 char * e10 = (char *) & x[1][0]; \
129 char * e20 = (char *) & x[2][0]; \
130 char * en0 = (char *) & x[N_ELEMS][0]; \
131 \
132 char * enn = (char *) & x[N_ELEMS][N_ELEMS]; \
133 \
134 ptrdiff_t d_00_01 = e01 - e00; \
135 ptrdiff_t d_01_02 = e02 - e01; \
136 ptrdiff_t d_00_02 = e02 - e00; \
137 ptrdiff_t d_00_0n = e0n - e00; \
138 \
139 ptrdiff_t d_00_10 = e10 - e00; \
140 ptrdiff_t d_10_20 = e20 - e10; \
141 ptrdiff_t d_00_20 = e20 - e00; \
142 ptrdiff_t d_00_n0 = en0 - e00; \
143 \
144 ptrdiff_t d_00_nn = enn - e00; \
145 \
146 printf("Delta 0,0--0,1 expected %zd bytes, actual %zd bytes\n", 1 * 1 * expectedElmSz, d_00_01); \
147 printf("Delta 0,1--0,2 expected %zd bytes, actual %zd bytes\n", 1 * 1 * expectedElmSz, d_01_02); \
148 printf("Delta 0,0--0,2 expected %zd bytes, actual %zd bytes\n", 1 * 2 * expectedElmSz, d_00_02); \
149 printf("Delta 0,0--0,n expected %zd bytes, actual %zd bytes\n", 1 * N_ELEMS * expectedElmSz, d_00_0n); \
150 \
151 printf("Delta 0,0--1,0 expected %zd bytes, actual %zd bytes\n", N_ELEMS * 1 * expectedElmSz, d_00_10); \
152 printf("Delta 1,0--2,0 expected %zd bytes, actual %zd bytes\n", N_ELEMS * 1 * expectedElmSz, d_10_20); \
153 printf("Delta 0,0--2,0 expected %zd bytes, actual %zd bytes\n", N_ELEMS * 2 * expectedElmSz, d_00_20); \
154 printf("Delta 0,0--n,0 expected %zd bytes, actual %zd bytes\n", N_ELEMS * N_ELEMS * expectedElmSz, d_00_n0); \
155 \
156 printf("Delta 0,0--n,n expected %zd bytes, actual %zd bytes\n", N_ELEMS * N_ELEMS * expectedElmSz + \
157 1 * N_ELEMS * expectedElmSz, d_00_nn); \
158 \
159 VPRT( "Array start %p end %p\n", e00, enn ); \
160 \
161 ar_lo = e00; \
162 ar_hi = en0; /* first byte past the end is not after the first row that does not exist */
163
164
165
166
167
168// ---------- 1, singleton
169
170forall( T ) T * allocAndAccess_1 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
171 printf("------- 1%s (singleton): T x[1], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
172 T x[ 1 ] INITARR;
173 bookendInner();
174 SHOW_ACCESS_1D( 1 )
175 return 0p;
176}
177
178// ---------- 2, general
179
180forall( T ) T * allocAndAccess_2 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
181 printf("------- 2%s (general): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
182 T x[ 42 ] INITARR;
183 bookendInner();
184 SHOW_ACCESS_1D( 42 )
185 return 0p;
186}
187
188// ---------- 3, user VLA
189
190forall( T ) T * allocAndAccess_3 ( size_t expectedElmSz, const char * tcid, const char * vart, size_t n ) {
191 printf("------- 3%s (user VLA): T x[n], got n=%zd, expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, n, vart, sizeof(T), expectedElmSz);
192 T x[ n ] INITARR;
193 bookendInner();
194 SHOW_ACCESS_1D( n )
195 return 0p;
196}
197
198// ---------- 4, 2-dimensional
199
200forall( T ) T * allocAndAccess_4 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
201 printf("------- 4%s (2-dimensional): T x[42][42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte atoms\n", tcid, vart, sizeof(T), expectedElmSz);
202 T x[ 42 ][ 42 ] INITARR;
203 bookendInner();
204 SHOW_ACCESS_2D( 42 )
205 return 0p;
206}
207
208// ---------- 5, pair
209
210forall( T ) T * allocAndAccess_5 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
211 printf("------- 5%s (pair): pair(T,T) x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte atoms\n", tcid, vart, sizeof(T), expectedElmSz);
212 pair(T,T) x[ 42 ] INITARR;
213 bookendInner();
214 SHOW_ACCESS_1D( 42 )
215 return 0p;
216}
217
218// ---------- 6, raii
219
220struct my_mgd_t {
221 float x;
222};
223
224// Auxiliary state used in the RAII rig only. Only to format/excerpt output. Reset per TC.
225static struct {
226 size_t total_elems; // size of array being managed
227 size_t ctor_calls; // number of ctor calls seen so far
228 size_t dtor_calls; // ^dtor
229 char * ctor_first; // argument of first ctor call
230 char * dtor_first; // ^dtor
231 char * dtor_lo; // lowest dtor argument seen yet
232 char * dtor_hi; // ^highest
233} raii;
234
235void ?{}( my_mgd_t & this ) {
236 if (raii.ctor_first == 0p) raii.ctor_first = (char *) & this;
237 VPRT( "ctor call %zd targets %p\n", raii.ctor_calls, &this );
238 if (raii.ctor_calls < 2 || raii.total_elems - raii.ctor_calls <= 2)
239 printf( "ctor call %zd targets first + %zd bytes\n", raii.ctor_calls, ((char*)&this - raii.ctor_first) );
240 // ctor call locations fill the conformed ar_lo/hi
241 if ( (char *) & this < ar_lo ) ar_lo = (char *) & this;
242 if ( (char *) & this > ar_hi ) ar_hi = (char *) & this;
243 raii.ctor_calls += 1;
244}
245
246void ^?{}( my_mgd_t & this ) {
247 // dtor calls count backward
248 if (raii.dtor_first == 0p) raii.dtor_first = (char *) & this;
249 VPRT( "dtor call %zd targets %p\n", raii.dtor_calls, &this );
250 if (raii.dtor_calls < 2 || raii.total_elems - raii.dtor_calls <= 2)
251 printf( "dtor call %zd targets first - %zd bytes\n", raii.dtor_calls, (raii.dtor_first - (char*)&this) );
252 // dtor call locations fill auxiliary state; reconciled with the conformed ones on last call
253 if ( (char *) & this < raii.dtor_lo ) raii.dtor_lo = (char *) & this;
254 if ( (char *) & this > raii.dtor_hi ) raii.dtor_hi = (char *) & this;
255 raii.dtor_calls += 1;
256 if (raii.dtor_calls >= raii.total_elems)
257 printf( "dtor lo off by %zd bytes, hi off by %zd bytes\n", (ar_lo - raii.dtor_lo), (ar_hi - raii.dtor_hi) );
258}
259
260forall( T ) T * allocAndAccess_6 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
261 raii.total_elems = 42;
262 raii.ctor_calls = 0;
263 raii.dtor_calls = 0;
264 raii.ctor_first = 0p;
265 raii.dtor_first = 0p;
266 raii.dtor_lo = (char*)-1;
267 raii.dtor_hi = 0p;
268 printf("------- 6%s (raii): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
269 T x[ 42 ] INITARR;
270 bookendInner();
271 // no SHOW_ACCESS: it happens in the cdtors
272 return 0p;
273}
274
275// ---------- 7, comm, PPD, PFST
276
277forall( T* ) void access_7 ( size_t expectedElmSz, T x[] ) {
278 SHOW_ACCESS_1D(42)
279}
280forall( T ) T * allocAndAccess_7 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
281 printf("------- 7%s (communication, poly-poly direct, by param T[]): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
282 T x[ 42 ] INITARR;
283 bookendInner();
284 access_7( expectedElmSz, x );
285 return 0p;
286}
287
288// ---------- 8, comm, PPD, PARR
289
290forall( T* ) void access_8 ( size_t expectedElmSz, T (*temp)[42] ) {
291 T * x = *temp;
292 SHOW_ACCESS_1D(42)
293}
294forall( T ) T * allocAndAccess_8 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
295 printf("------- 8%s (communication, poly-poly direct, by param T(*)[*]): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
296 T x[ 42 ] INITARR;
297 bookendInner();
298 access_8( expectedElmSz, &x );
299 return 0p;
300}
301
302// ---------- 9, comm, PPA, PFST
303
304forall( T | { void access_9 ( size_t, T x[] ); } )
305T * allocAndAccess_9 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
306 printf("------- 9%s (communication, poly-poly assertion, by param T[]): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
307 T x[ 42 ] INITARR;
308 bookendInner();
309 access_9( expectedElmSz, x );
310 return 0p;
311}
312forall( T* ) void access_9 ( size_t expectedElmSz, T x[] ) {
313 SHOW_ACCESS_1D(42)
314}
315
316// ---------- 10, comm, PPA, PARR
317
318forall( T | { void access_10 ( size_t, T (*)[42] ); } )
319T * allocAndAccess_10( size_t expectedElmSz, const char * tcid, const char * vart ) {
320 printf("------- 10%s (communication, poly-poly assertion, by param T(*)[*]): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
321 T x[ 42 ] INITARR;
322 bookendInner();
323 access_10( expectedElmSz, &x );
324 return 0p;
325}
326forall( T* ) void access_10( size_t expectedElmSz, T (*temp)[42] ) {
327 T * x = *temp;
328 SHOW_ACCESS_1D(42)
329}
330
331// ---------- 11, comm, PMA, PFST_11
332
333forall( T | { void access_11( size_t, T * ); } )
334T * allocAndAccess_11 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
335 printf("------- 11%s (communication, poly-mono assertion, by param T[]): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
336 T x[ 42 ] INITARR;
337 bookendInner();
338 access_11( expectedElmSz, x );
339 return 0p;
340}
341void access_11 ( size_t expectedElmSz, char x[] ) {
342 SHOW_ACCESS_1D(42)
343}
344void access_11 ( size_t expectedElmSz, bigun x[] ) {
345 SHOW_ACCESS_1D(42)
346}
347
348// ---------- 12, comm, PMA, PARR
349
350forall( T | { void access_12 ( size_t, T (*)[42] ); } )
351T * allocAndAccess_12 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
352 printf("------- 12%s (communication, poly-mono assertion, by param T(*)[*]): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
353 T x[ 42 ] INITARR;
354 bookendInner();
355 access_12( expectedElmSz, &x );
356 return 0p;
357}
358void access_12 ( size_t expectedElmSz, double (*temp)[42] ) {
359 double * x = *temp;
360 SHOW_ACCESS_1D(42)
361}
362
363// ---------- 13, comm, MPD, PFST
364
365forall( T* ) void access_13( size_t expectedElmSz, T x[] ) {
366 SHOW_ACCESS_1D(42)
367}
368char * allocAndAccess_13 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
369 printf("------- 13%s (communication, mono-poly direct, by param T[]): char x[42], expecting %zd-byte elems\n", tcid, expectedElmSz);
370 char x[ 42 ] INITARR;
371 bookendInner();
372 access_13( expectedElmSz, x );
373 return 0p;
374}
375bigun * allocAndAccess_13( size_t expectedElmSz, const char * tcid, const char * vart ) {
376 printf("------- 13%s (communication, mono-poly direct, by param T[]): bigun x[42], expecting %zd-byte elems\n", tcid, expectedElmSz);
377 bigun x[ 42 ] INITARR;
378 bookendInner();
379 access_13( expectedElmSz, x );
380 return 0p;
381}
382
383// ---------- 14, comm, MPD, PARR
384
385forall( T* ) void access_14 ( size_t expectedElmSz, T (*temp)[42] ) {
386 T * x = *temp;
387 SHOW_ACCESS_1D(42)
388}
389double * allocAndAccess_14 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
390 printf("------- 13%s (communication, mono-poly direct, by param T(*)[*]): double x[42], expecting %zd-byte elems\n", tcid, expectedElmSz);
391 double x[ 42 ] INITARR;
392 bookendInner();
393 access_14( expectedElmSz, &x );
394 return 0p;
395}
396
397// ---------- 15, operators
398
399forall( T* ) void access_15 ( size_t expectedElmSz, T x[] ) {
400 // correctness of x and ?[?] established by earlier tests
401 T * x5 = & x[5];
402
403 #define SHOW( OP, ACT, EXP ) printf( #OP " off by %zd\n", ((size_t)(EXP)) - ((size_t)(ACT)) )
404 { T * xx = & 5[x]; SHOW( ?[?] rev, xx, x5 ); }
405 { T * xx = x + 5; SHOW( ?+?, xx, x5 ); }
406 { T * xx = 5 + x; SHOW( ?+? rev, xx, x5 ); }
407 { T * xx = x; xx += 5; SHOW( ?+=?, xx, x5 ); }
408// { T * xx = x; for(5) xx++; SHOW( ?++, xx, x5 ); }
409// { T * xx = x; for(5) ++xx; SHOW( ++?, xx, x5 ); }
410 { T * xx = x5; xx -= 5; SHOW( ?-=?, xx, x ); }
411// { T * xx = x5; for(5) xx--; SHOW( ?--, xx, x ); }
412// { T * xx = x5; for(5) --xx; SHOW( --?, xx, x ); }
413 #undef SHOW
414
415 ptrdiff_t expPos5 = x5 - x;
416 ptrdiff_t expNeg5 = x - x5;
417
418 printf( "?-? +ve off by %zd\n", ((ptrdiff_t) 5) - expPos5 );
419// printf( "?-? -ve off by %zd\n", ((ptrdiff_t)-5) - expNeg5 );
420}
421
422forall( T ) T * allocAndAccess_15 ( size_t expectedElmSz, const char * tcid, const char * vart ) {
423 printf("------- 15%s (operators): T x[42], expecting T=%s, got sizeof(T)=%zd, expecting %zd-byte elems\n", tcid, vart, sizeof(T), expectedElmSz);
424 T x[ 42 ] INITARR;
425 // bookends unused
426 access_15( expectedElmSz, x );
427 return 0p;
428}
429
430
431
432
433
434#define TC(...)
435#define TR( TRID, SZS, SZV, ETG, ACCS, SPS, OVLD ) \
436 F_SIG( run, TRID, SZS, SZV, ACCS, SPS, OVLD ) { \
437 resetBookends(); \
438 OVLD * retval = CALL( bookendOuter, TRID, SZS, SZV, expectedElmSz, tcid, vart ); \
439 reportBookends(); \
440 return retval; \
441 }
442#include "boxed.cases.hfa"
443#undef TC
444#undef TR
445
446
447#define Q_(x) #x
448#define Q(x) Q_(x)
449
450int main() {
451 #define TR(...)
452 #define TC( TRID, TCID, SZS, SZV, ETG, VART ) \
453 { VART * ignore = CALL( run, TRID, SZS, SZV, sizeof(ETG(VART)), Q(TCID), Q(VART) ); (void) ignore; }
454 #include "boxed.cases.hfa"
455 #undef TR
456 #undef TC
457}
Note: See TracBrowser for help on using the repository browser.