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

Last change on this file since fd4df379 was fd4df379, checked in by Michael Brooks <mlbrooks@…>, 4 days 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
RevLine 
[fd4df379]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.