mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-08 08:26:14 +00:00
649 lines
26 KiB
C
649 lines
26 KiB
C
|
|
/* A Memcheck test program for conditional loads and stores,
|
|
as shown in do_conditional_{load,store}32.
|
|
|
|
Program is run twice, once for loads and once for stores, only
|
|
because each run generates 80 errors, and we want to see them all.
|
|
Doing both loads and stores in each run runs into the problem that
|
|
errors are more aggressively commoned up after the 100th, and so
|
|
some that we want to see aren't shown. Splitting the run into two
|
|
pieces avoids this.
|
|
|
|
On ARM we hardwire genuine conditional loads and stores to be
|
|
tested -- which is the real point of this test, since we are sure
|
|
they will turn into IRLoadG/IRStoreG. On other platforms we make
|
|
do with whatever gcc gives us for the equivalent C fragment. In
|
|
both cases Memcheck's results should be identical -- at least in
|
|
error counts; line numbers unfortunately will differ. Hence there
|
|
are -arm and -non-arm expected output files. */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include "../memcheck.h"
|
|
|
|
typedef unsigned int UInt;
|
|
|
|
typedef unsigned char Bool;
|
|
#define False ((Bool)0)
|
|
#define True ((Bool)1)
|
|
|
|
static void make_undef ( void* addr, size_t len )
|
|
{
|
|
(void) VALGRIND_MAKE_MEM_UNDEFINED(addr, len);
|
|
}
|
|
|
|
static void make_def ( void* addr, size_t len )
|
|
{
|
|
(void) VALGRIND_MAKE_MEM_DEFINED(addr, len);
|
|
}
|
|
|
|
// Returns either |*src| or |alt|.
|
|
__attribute__((noinline))
|
|
UInt do_conditional_load32 ( UInt* src, UInt alt, Bool b )
|
|
{
|
|
UInt res;
|
|
# if defined(__linux__) && defined(__arm__)
|
|
__asm__ __volatile__(
|
|
"mov r5, %2" "\n\t" // alt
|
|
"tst %3, #0xFF" "\n\t" // b
|
|
"it ne" "\n\t"
|
|
"ldrne r5, [%1]" "\n\t" // src
|
|
"mov %0, r5" "\n\t" // res
|
|
: /*OUT*/"=r"(res)
|
|
: /*IN*/"r"(src), "r"(alt), "r"(b)
|
|
: /*TRASH*/ "r5","cc","memory"
|
|
);
|
|
# else
|
|
__asm__ __volatile__("" ::: "cc","memory");
|
|
res = b ? *src : alt;
|
|
# endif
|
|
// res might be undefined. Paint it as defined so the
|
|
// caller can look at it without invoking further errors.
|
|
make_def(&res, sizeof(res));
|
|
return res;
|
|
}
|
|
|
|
// Possibly writes |alt| to |*dst|, and returns the resulting
|
|
// value of |*dst|.
|
|
__attribute__((noinline))
|
|
UInt do_conditional_store32 ( UInt* dst, UInt alt, Bool b )
|
|
{
|
|
# if defined(__linux__) && defined(__arm__)
|
|
__asm__ __volatile__(
|
|
"mov r5, %1" "\n\t" // alt
|
|
"tst %2, #0xFF" "\n\t" // b
|
|
"it ne" "\n\t"
|
|
"strne r5, [%0]" "\n\t" // dst
|
|
: /*OUT*/
|
|
: /*IN*/"r"(dst), "r"(alt), "r"(b)
|
|
: /*TRASH*/ "r5","cc","memory"
|
|
);
|
|
# else
|
|
__asm__ __volatile__("" ::: "cc","memory");
|
|
if (b) *dst = alt;
|
|
# endif
|
|
/* Now we need to get hold of the value at *dst. But it might be
|
|
unaddressible and/or undefined. Hence turn off error reporting
|
|
when getting it. */
|
|
UInt res;
|
|
VALGRIND_DISABLE_ERROR_REPORTING;
|
|
res = *dst;
|
|
VALGRIND_ENABLE_ERROR_REPORTING;
|
|
make_def(&res, sizeof(res));
|
|
return res;
|
|
}
|
|
|
|
|
|
/* --- LOAD ----------------------------------------- LOAD --- */
|
|
/* --- LOAD ----------------------------------------- LOAD --- */
|
|
/* --- LOAD ----------------------------------------- LOAD --- */
|
|
|
|
/* For conditional loads, there are 64 combinations to test.
|
|
|
|
cond: { defined-true, defined-false,
|
|
undefined-true, undefined-false } D1 D0 U1 U0
|
|
x
|
|
addr: { defined-valid, defined-invalid,
|
|
undefined-valid, undefined-invalid } DV DI UV UI
|
|
x
|
|
alt: { defined, undefined } Da Ub
|
|
x
|
|
data: { defined, undefined } Dc Ud
|
|
|
|
// a, b, c, d refer to actual values
|
|
|
|
The general form of the test is:
|
|
1. Place data at *addr
|
|
2. return "cond ? *addr : alt"
|
|
*/
|
|
typedef enum { Cond_D1=10, Cond_D0, Cond_U1, Cond_U0 } Inp_Cond;
|
|
typedef enum { Addr_DV=20, Addr_DI, Addr_UV, Addr_UI } Inp_Addr;
|
|
typedef enum { Alt_Da=30, Alt_Ub } Inp_Alt;
|
|
typedef enum { Data_Dc=40, Data_Ud } Inp_Data;
|
|
|
|
typedef
|
|
struct { Inp_Cond inp_Cond; Inp_Addr inp_Addr;
|
|
Inp_Alt inp_Alt; Inp_Data inp_Data;
|
|
char res; char defErr_Cond; char defErr_Addr; char addrErr; }
|
|
TestCase;
|
|
|
|
const TestCase loadCases[64] = {
|
|
|
|
// ADDR ALT COND DATA Res
|
|
// defErr-COND
|
|
// defErr-ADDR
|
|
// addrErr
|
|
|
|
// In all of the next 16 cases, the load definitely happens
|
|
// and |alt| is therefore irrelevant
|
|
{ Cond_D1, Addr_DV, Alt_Da, Data_Dc, 'C', 'N', 'N', 'N' }, // 0
|
|
{ Cond_D1, Addr_DV, Alt_Da, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D1, Addr_DV, Alt_Ub, Data_Dc, 'C', 'N', 'N', 'N' },
|
|
{ Cond_D1, Addr_DV, Alt_Ub, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D1, Addr_DI, Alt_Da, Data_Dc, 'C', 'N', 'N', 'Y' },
|
|
{ Cond_D1, Addr_DI, Alt_Da, Data_Ud, 'D', 'N', 'N', 'Y' },
|
|
{ Cond_D1, Addr_DI, Alt_Ub, Data_Dc, 'C', 'N', 'N', 'Y' },
|
|
{ Cond_D1, Addr_DI, Alt_Ub, Data_Ud, 'D', 'N', 'N', 'Y' },
|
|
|
|
{ Cond_D1, Addr_UV, Alt_Da, Data_Dc, 'C', 'N', 'Y', 'N' }, // 8
|
|
{ Cond_D1, Addr_UV, Alt_Da, Data_Ud, 'D', 'N', 'Y', 'N' },
|
|
{ Cond_D1, Addr_UV, Alt_Ub, Data_Dc, 'C', 'N', 'Y', 'N' },
|
|
{ Cond_D1, Addr_UV, Alt_Ub, Data_Ud, 'D', 'N', 'Y', 'N' },
|
|
{ Cond_D1, Addr_UI, Alt_Da, Data_Dc, 'C', 'N', 'Y', 'Y' },
|
|
{ Cond_D1, Addr_UI, Alt_Da, Data_Ud, 'D', 'N', 'Y', 'Y' },
|
|
{ Cond_D1, Addr_UI, Alt_Ub, Data_Dc, 'C', 'N', 'Y', 'Y' },
|
|
{ Cond_D1, Addr_UI, Alt_Ub, Data_Ud, 'D', 'N', 'Y', 'Y' },
|
|
|
|
// In the next 16 cases, the load definitely does not happen,
|
|
// so we just return |alt|.
|
|
{ Cond_D0, Addr_DV, Alt_Da, Data_Dc, 'A', 'N', 'N', 'N' }, // 16
|
|
{ Cond_D0, Addr_DV, Alt_Da, Data_Ud, 'A', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DV, Alt_Ub, Data_Dc, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DV, Alt_Ub, Data_Ud, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Da, Data_Dc, 'A', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Da, Data_Ud, 'A', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Ub, Data_Dc, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Ub, Data_Ud, 'B', 'N', 'N', 'N' },
|
|
|
|
{ Cond_D0, Addr_UV, Alt_Da, Data_Dc, 'A', 'N', 'N', 'N' }, // 24
|
|
{ Cond_D0, Addr_UV, Alt_Da, Data_Ud, 'A', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UV, Alt_Ub, Data_Dc, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UV, Alt_Ub, Data_Ud, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Da, Data_Dc, 'A', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Da, Data_Ud, 'A', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Ub, Data_Dc, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Ub, Data_Ud, 'B', 'N', 'N', 'N' },
|
|
|
|
// ADDR ALT COND DATA Res
|
|
// defErr-COND
|
|
// defErr-ADDR
|
|
// addrErr
|
|
|
|
// In the next 16 cases, the load happens, but the condition
|
|
// is undefined. This means that it should behave like the
|
|
// first group of 16 cases, except that we should also get a
|
|
// complaint about the definedness of the condition.
|
|
{ Cond_U1, Addr_DV, Alt_Da, Data_Dc, 'C', 'Y', 'N', 'N' }, // 32
|
|
{ Cond_U1, Addr_DV, Alt_Da, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U1, Addr_DV, Alt_Ub, Data_Dc, 'C', 'Y', 'N', 'N' },
|
|
{ Cond_U1, Addr_DV, Alt_Ub, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U1, Addr_DI, Alt_Da, Data_Dc, 'C', 'Y', 'N', 'Y' },
|
|
{ Cond_U1, Addr_DI, Alt_Da, Data_Ud, 'D', 'Y', 'N', 'Y' },
|
|
{ Cond_U1, Addr_DI, Alt_Ub, Data_Dc, 'C', 'Y', 'N', 'Y' },
|
|
{ Cond_U1, Addr_DI, Alt_Ub, Data_Ud, 'D', 'Y', 'N', 'Y' },
|
|
|
|
{ Cond_U1, Addr_UV, Alt_Da, Data_Dc, 'C', 'Y', 'Y', 'N' }, // 40
|
|
{ Cond_U1, Addr_UV, Alt_Da, Data_Ud, 'D', 'Y', 'Y', 'N' },
|
|
{ Cond_U1, Addr_UV, Alt_Ub, Data_Dc, 'C', 'Y', 'Y', 'N' },
|
|
{ Cond_U1, Addr_UV, Alt_Ub, Data_Ud, 'D', 'Y', 'Y', 'N' },
|
|
{ Cond_U1, Addr_UI, Alt_Da, Data_Dc, 'C', 'Y', 'Y', 'Y' },
|
|
{ Cond_U1, Addr_UI, Alt_Da, Data_Ud, 'D', 'Y', 'Y', 'Y' },
|
|
{ Cond_U1, Addr_UI, Alt_Ub, Data_Dc, 'C', 'Y', 'Y', 'Y' },
|
|
{ Cond_U1, Addr_UI, Alt_Ub, Data_Ud, 'D', 'Y', 'Y', 'Y' },
|
|
|
|
// In this last group of 16 cases, the load does not happen,
|
|
// but the condition is undefined. So we just return |alt|,
|
|
// and also complain about the condition. Hence it's like the
|
|
// second group of 16 cases except that we also get a complaint
|
|
// about the condition.
|
|
{ Cond_U0, Addr_DV, Alt_Da, Data_Dc, 'A', 'Y', 'N', 'N' }, // 48
|
|
{ Cond_U0, Addr_DV, Alt_Da, Data_Ud, 'A', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DV, Alt_Ub, Data_Dc, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DV, Alt_Ub, Data_Ud, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Da, Data_Dc, 'A', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Da, Data_Ud, 'A', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Ub, Data_Dc, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Ub, Data_Ud, 'B', 'Y', 'N', 'N' },
|
|
|
|
{ Cond_U0, Addr_UV, Alt_Da, Data_Dc, 'A', 'Y', 'N', 'N' }, // 56
|
|
{ Cond_U0, Addr_UV, Alt_Da, Data_Ud, 'A', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UV, Alt_Ub, Data_Dc, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UV, Alt_Ub, Data_Ud, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Da, Data_Dc, 'A', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Da, Data_Ud, 'A', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Ub, Data_Dc, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Ub, Data_Ud, 'B', 'Y', 'N', 'N' } // 63
|
|
};
|
|
|
|
// Constant, corresponding to the test enums
|
|
static Bool c_Cond_D1, c_Cond_D0, c_Cond_U1, c_Cond_U0;
|
|
static UInt *c_Addr_DV, *c_Addr_DI, *c_Addr_UV, *c_Addr_UI;
|
|
static UInt c_Alt_Da, c_Alt_Ub;
|
|
|
|
static void setup_test_data ( Inp_Data inp_Data )
|
|
{
|
|
c_Cond_D1 = c_Cond_U1 = True;
|
|
c_Cond_D0 = c_Cond_U0 = False;
|
|
make_undef(&c_Cond_U1, sizeof(c_Cond_U1));
|
|
make_undef(&c_Cond_U0, sizeof(c_Cond_U0));
|
|
|
|
c_Addr_DV = c_Addr_UV = malloc(4);
|
|
c_Addr_DI = c_Addr_UI = malloc(4);
|
|
// install test data at the given address
|
|
UInt testd = inp_Data == Data_Dc ? 0xCCCCCCCC : 0xDDDDDDDD;
|
|
*c_Addr_DV = *c_Addr_DI = testd;
|
|
if (inp_Data == Data_Dc) {
|
|
// it's already defined
|
|
} else {
|
|
make_undef(c_Addr_DV, 4);
|
|
make_undef(c_Addr_DI, 4);
|
|
}
|
|
|
|
// make the invalid address invalid. This unfortunately loses
|
|
// the definedness state of the data that is stored there.
|
|
free(c_Addr_DI);
|
|
|
|
// and set the definedness of the pointers themselves.
|
|
make_undef(&c_Addr_UV, sizeof(c_Addr_UV));
|
|
make_undef(&c_Addr_UI, sizeof(c_Addr_UI));
|
|
|
|
// and set up alt
|
|
c_Alt_Da = 0xAAAAAAAA;
|
|
c_Alt_Ub = 0xBBBBBBBB;
|
|
make_undef(&c_Alt_Ub, sizeof(c_Alt_Ub));
|
|
}
|
|
|
|
static void do_test_case ( int caseNo, Bool isLoad, const TestCase* lc )
|
|
{
|
|
fprintf(stderr,
|
|
"\n-----------------------------------------------------------\n");
|
|
fprintf(stderr, "%s CASE %d\n", isLoad ? "LOAD" : "STORE", caseNo);
|
|
// validate ..
|
|
assert(Cond_D1 <= lc->inp_Cond && lc->inp_Cond <= Cond_U0);
|
|
assert(Addr_DV <= lc->inp_Addr && lc->inp_Addr <= Addr_UI);
|
|
assert(lc->inp_Alt == Alt_Da || lc->inp_Alt == Alt_Ub);
|
|
assert(lc->inp_Data == Data_Dc || lc->inp_Data == Data_Ud);
|
|
assert('A' <= lc->res && lc->res <= 'D');
|
|
assert(lc->defErr_Cond == 'Y' || lc->defErr_Cond == 'N');
|
|
assert(lc->defErr_Addr == 'Y' || lc->defErr_Addr == 'N');
|
|
assert(lc->addrErr == 'Y' || lc->addrErr == 'N');
|
|
// set up test data constants
|
|
setup_test_data(lc->inp_Data);
|
|
|
|
// and select constants for the test, depending on |lc|
|
|
// Except, skip i_Data since setup_test_data takes care of it.
|
|
Bool i_Cond;
|
|
UInt* i_Addr;
|
|
UInt i_Alt;
|
|
switch (lc->inp_Cond) {
|
|
case Cond_D1: i_Cond = c_Cond_D1; break;
|
|
case Cond_D0: i_Cond = c_Cond_D0; break;
|
|
case Cond_U1: i_Cond = c_Cond_U1; break;
|
|
case Cond_U0: i_Cond = c_Cond_U0; break;
|
|
default: assert(0);
|
|
}
|
|
switch (lc->inp_Addr) {
|
|
case Addr_DV: i_Addr = c_Addr_DV; break;
|
|
case Addr_DI: i_Addr = c_Addr_DI; break;
|
|
case Addr_UV: i_Addr = c_Addr_UV; break;
|
|
case Addr_UI: i_Addr = c_Addr_UI; break;
|
|
default: assert(0);
|
|
}
|
|
switch (lc->inp_Alt) {
|
|
case Alt_Da: i_Alt = c_Alt_Da; break;
|
|
case Alt_Ub: i_Alt = c_Alt_Ub; break;
|
|
default: assert(0);
|
|
}
|
|
|
|
// How many errors do we expect from this?
|
|
UInt n_errs_exp
|
|
= (lc->defErr_Cond == 'Y' ? 1 : 0) + (lc->defErr_Addr == 'Y' ? 1 : 0)
|
|
+ (lc->addrErr == 'Y' ? 1 : 0);
|
|
|
|
UInt n_errs_act = VALGRIND_COUNT_ERRORS;
|
|
UInt res_act;
|
|
if (isLoad) {
|
|
res_act = do_conditional_load32(i_Addr, i_Alt, i_Cond);
|
|
} else {
|
|
res_act = do_conditional_store32(i_Addr, i_Alt, i_Cond);
|
|
}
|
|
n_errs_act = VALGRIND_COUNT_ERRORS - n_errs_act;
|
|
|
|
if (n_errs_act == n_errs_exp) {
|
|
fprintf(stderr, "PASS: %u errors\n", n_errs_act);
|
|
} else {
|
|
fprintf(stderr, "FAIL: %u errors expected, %u actual\n",
|
|
n_errs_exp, n_errs_act);
|
|
}
|
|
|
|
// What's the expected result value (actual loaded data?)
|
|
UInt res_exp = 0;
|
|
switch (lc->res) {
|
|
case 'A': res_exp = 0xAAAAAAAA; break;
|
|
case 'B': res_exp = 0xBBBBBBBB; break;
|
|
case 'C': res_exp = 0xCCCCCCCC; break;
|
|
case 'D': res_exp = 0xDDDDDDDD; break;
|
|
default: assert(0);
|
|
}
|
|
|
|
if (res_act == res_exp) {
|
|
fprintf(stderr, "PASS: correct result\n");
|
|
} else {
|
|
fprintf(stderr, "FAIL: result: %08x expected, %08x actual\n",
|
|
res_exp, res_act);
|
|
}
|
|
|
|
free(c_Addr_DV);
|
|
}
|
|
|
|
|
|
void do_test_case_steer ( void (*fn)(int,Bool,const TestCase*),
|
|
int i, Bool isLd, const TestCase* tc )
|
|
{
|
|
__asm__ __volatile__("");
|
|
if (i == 0) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 1) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 2) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 3) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 4) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 5) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 6) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 7) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 8) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 9) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 10) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 11) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 12) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 13) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 14) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 15) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 16) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 17) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 18) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 19) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 20) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 21) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 22) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 23) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 24) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 25) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 26) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 27) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 28) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 29) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 30) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 31) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 32) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 33) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 34) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 35) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 36) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 37) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 38) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 39) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 40) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 41) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 42) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 43) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 44) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 45) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 46) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 47) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 48) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 49) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 50) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 51) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 52) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 53) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 54) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 55) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 56) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 57) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 58) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 59) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 60) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 61) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 62) { fn(i,isLd,tc); return; };
|
|
__asm__ __volatile__("");
|
|
if (i == 63) { fn(i,isLd,tc); return; };
|
|
assert(0);
|
|
}
|
|
|
|
|
|
/* --- STORE --------------------------------------- STORE --- */
|
|
/* --- STORE --------------------------------------- STORE --- */
|
|
/* --- STORE --------------------------------------- STORE --- */
|
|
|
|
/* For conditional stores, there are 64 combinations to test.
|
|
|
|
cond: { defined-true, defined-false,
|
|
undefined-true, undefined-false } D1 D0 U1 U0
|
|
x
|
|
addr: { defined-valid, defined-invalid,
|
|
undefined-valid, undefined-invalid } DV DI UV UI
|
|
x
|
|
alt: { defined, undefined } Da Ub
|
|
x
|
|
data: { defined, undefined } Dc Ud
|
|
|
|
// a, b, c, d refer to actual values
|
|
|
|
The general form of the test is:
|
|
1. Place data at *addr
|
|
2. do "if (cond) *addr = alt"
|
|
3 return *addr
|
|
|
|
Hence identical setup to the load cases, although the roles of
|
|
data and alt are somewhat confusingly swapped. |data| here is
|
|
the "didn't happen" result, and |alt| is the "did happen" result.
|
|
*/
|
|
|
|
const TestCase storeCases[64] = {
|
|
|
|
// ADDR ALT COND DATA Res
|
|
// defErr-COND
|
|
// defErr-ADDR
|
|
// addrErr
|
|
|
|
// In all of the next 16 cases, the store definitely happens
|
|
// and |data| is therefore irrelevant
|
|
{ Cond_D1, Addr_DV, Alt_Da, Data_Dc, 'A', 'N', 'N', 'N' }, // 0
|
|
{ Cond_D1, Addr_DV, Alt_Da, Data_Ud, 'A', 'N', 'N', 'N' },
|
|
{ Cond_D1, Addr_DV, Alt_Ub, Data_Dc, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D1, Addr_DV, Alt_Ub, Data_Ud, 'B', 'N', 'N', 'N' },
|
|
{ Cond_D1, Addr_DI, Alt_Da, Data_Dc, 'A', 'N', 'N', 'Y' },
|
|
{ Cond_D1, Addr_DI, Alt_Da, Data_Ud, 'A', 'N', 'N', 'Y' },
|
|
{ Cond_D1, Addr_DI, Alt_Ub, Data_Dc, 'B', 'N', 'N', 'Y' },
|
|
{ Cond_D1, Addr_DI, Alt_Ub, Data_Ud, 'B', 'N', 'N', 'Y' },
|
|
|
|
{ Cond_D1, Addr_UV, Alt_Da, Data_Dc, 'A', 'N', 'Y', 'N' }, // 8
|
|
{ Cond_D1, Addr_UV, Alt_Da, Data_Ud, 'A', 'N', 'Y', 'N' },
|
|
{ Cond_D1, Addr_UV, Alt_Ub, Data_Dc, 'B', 'N', 'Y', 'N' },
|
|
{ Cond_D1, Addr_UV, Alt_Ub, Data_Ud, 'B', 'N', 'Y', 'N' },
|
|
{ Cond_D1, Addr_UI, Alt_Da, Data_Dc, 'A', 'N', 'Y', 'Y' },
|
|
{ Cond_D1, Addr_UI, Alt_Da, Data_Ud, 'A', 'N', 'Y', 'Y' },
|
|
{ Cond_D1, Addr_UI, Alt_Ub, Data_Dc, 'B', 'N', 'Y', 'Y' },
|
|
{ Cond_D1, Addr_UI, Alt_Ub, Data_Ud, 'B', 'N', 'Y', 'Y' },
|
|
|
|
// In the next 16 cases, the store definitely does not happen,
|
|
// so we just return |data|.
|
|
{ Cond_D0, Addr_DV, Alt_Da, Data_Dc, 'C', 'N', 'N', 'N' }, // 16
|
|
{ Cond_D0, Addr_DV, Alt_Da, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DV, Alt_Ub, Data_Dc, 'C', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DV, Alt_Ub, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Da, Data_Dc, 'C', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Da, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Ub, Data_Dc, 'C', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_DI, Alt_Ub, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
|
|
{ Cond_D0, Addr_UV, Alt_Da, Data_Dc, 'C', 'N', 'N', 'N' }, // 24
|
|
{ Cond_D0, Addr_UV, Alt_Da, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UV, Alt_Ub, Data_Dc, 'C', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UV, Alt_Ub, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Da, Data_Dc, 'C', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Da, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Ub, Data_Dc, 'C', 'N', 'N', 'N' },
|
|
{ Cond_D0, Addr_UI, Alt_Ub, Data_Ud, 'D', 'N', 'N', 'N' },
|
|
|
|
// ADDR ALT COND DATA Res
|
|
// defErr-COND
|
|
// defErr-ADDR
|
|
// addrErr
|
|
|
|
// In the next 16 cases, the store happens, but the condition
|
|
// is undefined. This means that it should behave like the
|
|
// first group of 16 cases, except that we should also get a
|
|
// complaint about the definedness of the condition.
|
|
{ Cond_U1, Addr_DV, Alt_Da, Data_Dc, 'A', 'Y', 'N', 'N' }, // 32
|
|
{ Cond_U1, Addr_DV, Alt_Da, Data_Ud, 'A', 'Y', 'N', 'N' },
|
|
{ Cond_U1, Addr_DV, Alt_Ub, Data_Dc, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U1, Addr_DV, Alt_Ub, Data_Ud, 'B', 'Y', 'N', 'N' },
|
|
{ Cond_U1, Addr_DI, Alt_Da, Data_Dc, 'A', 'Y', 'N', 'Y' },
|
|
{ Cond_U1, Addr_DI, Alt_Da, Data_Ud, 'A', 'Y', 'N', 'Y' },
|
|
{ Cond_U1, Addr_DI, Alt_Ub, Data_Dc, 'B', 'Y', 'N', 'Y' },
|
|
{ Cond_U1, Addr_DI, Alt_Ub, Data_Ud, 'B', 'Y', 'N', 'Y' },
|
|
|
|
{ Cond_U1, Addr_UV, Alt_Da, Data_Dc, 'A', 'Y', 'Y', 'N' }, // 40
|
|
{ Cond_U1, Addr_UV, Alt_Da, Data_Ud, 'A', 'Y', 'Y', 'N' },
|
|
{ Cond_U1, Addr_UV, Alt_Ub, Data_Dc, 'B', 'Y', 'Y', 'N' },
|
|
{ Cond_U1, Addr_UV, Alt_Ub, Data_Ud, 'B', 'Y', 'Y', 'N' },
|
|
{ Cond_U1, Addr_UI, Alt_Da, Data_Dc, 'A', 'Y', 'Y', 'Y' },
|
|
{ Cond_U1, Addr_UI, Alt_Da, Data_Ud, 'A', 'Y', 'Y', 'Y' },
|
|
{ Cond_U1, Addr_UI, Alt_Ub, Data_Dc, 'B', 'Y', 'Y', 'Y' },
|
|
{ Cond_U1, Addr_UI, Alt_Ub, Data_Ud, 'B', 'Y', 'Y', 'Y' },
|
|
|
|
// In this last group of 16 cases, the store does not happen,
|
|
// but the condition is undefined. So we just return |data|,
|
|
// and also complain about the condition. Hence it's like the
|
|
// second group of 16 cases except that we also get a complaint
|
|
// about the condition.
|
|
{ Cond_U0, Addr_DV, Alt_Da, Data_Dc, 'C', 'Y', 'N', 'N' }, // 48
|
|
{ Cond_U0, Addr_DV, Alt_Da, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DV, Alt_Ub, Data_Dc, 'C', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DV, Alt_Ub, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Da, Data_Dc, 'C', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Da, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Ub, Data_Dc, 'C', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_DI, Alt_Ub, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
|
|
{ Cond_U0, Addr_UV, Alt_Da, Data_Dc, 'C', 'Y', 'N', 'N' }, // 56
|
|
{ Cond_U0, Addr_UV, Alt_Da, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UV, Alt_Ub, Data_Dc, 'C', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UV, Alt_Ub, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Da, Data_Dc, 'C', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Da, Data_Ud, 'D', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Ub, Data_Dc, 'C', 'Y', 'N', 'N' },
|
|
{ Cond_U0, Addr_UI, Alt_Ub, Data_Ud, 'D', 'Y', 'N', 'N' } // 63
|
|
};
|
|
|
|
void usage ( char* pname )
|
|
{
|
|
fprintf(stderr, "usage: %s [loads|stores]\n", pname);
|
|
exit(1);
|
|
}
|
|
|
|
int main ( int argc, char** argv )
|
|
{
|
|
UInt i, nCases;
|
|
|
|
if (argc != 2) usage(argv[0]);
|
|
|
|
Bool doLoad = False;
|
|
if (0 == strcmp(argv[1], "loads")) {
|
|
doLoad = True;
|
|
}
|
|
else if (0 == strcmp(argv[1], "stores")) {
|
|
doLoad = False;
|
|
}
|
|
else usage(argv[0]);
|
|
|
|
if (doLoad) {
|
|
nCases = sizeof(loadCases) / sizeof(loadCases[0]);
|
|
assert(nCases == 64);
|
|
for (i = 0; i < nCases; i++)
|
|
do_test_case_steer( do_test_case, i, True/*isLoad*/, &loadCases[i] );
|
|
} else {
|
|
nCases = sizeof(storeCases) / sizeof(storeCases[0]);
|
|
assert(nCases == 64);
|
|
for (i = 0; i < nCases; i++)
|
|
do_test_case_steer( do_test_case, i, False/*!isLoad*/, &storeCases[i] );
|
|
}
|
|
|
|
return 0;
|
|
}
|