-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_optim.c
More file actions
218 lines (190 loc) · 9.01 KB
/
Copy pathtest_optim.c
File metadata and controls
218 lines (190 loc) · 9.01 KB
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
/*
* test_optim.c — correctness + performance test for optimized cross-compiled binaries
*
* Tests: integer arithmetic, float/double ops (soft-float ABI), type-punning safety,
* alignment, memory ops, timing via clock_gettime, and basic system info.
*
* Build for NS: arm-linux-gnueabi-gcc -march=armv7-a -mtune=cortex-a53 \
* -mfloat-abi=soft -fomit-frame-pointer \
* -fno-strict-aliasing -O2 -o test_optim test_optim.c
* Build for XGS: aarch64-linux-gnu-gcc -march=armv8-a+crc -mcpu=cortex-a53 \
* -fomit-frame-pointer -fno-strict-aliasing -O2 \
* -o test_optim test_optim.c
* Build for H: mips-linux-musl-gcc -march=mips32r2 -mtune=34kc \
* -fomit-frame-pointer -fno-strict-aliasing -O2 -static \
* -o test_optim test_optim.c
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/utsname.h>
/* ── helpers ─────────────────────────────────────────────────────────────── */
static int tests_run = 0, tests_fail = 0;
#define CHECK(cond, label) do { \
tests_run++; \
if (!(cond)) { printf(" FAIL: %s\n", label); tests_fail++; } \
else { printf(" ok : %s\n", label); } \
} while(0)
static int64_t ns_elapsed(struct timespec a, struct timespec b) {
return (int64_t)(b.tv_sec - a.tv_sec) * 1000000000LL + (b.tv_nsec - a.tv_nsec);
}
/* ── type-punning helper (tests -fno-strict-aliasing safety) ─────────────── */
static uint32_t float_bits(float f) {
uint32_t u;
memcpy(&u, &f, 4); /* safe type-pun via memcpy */
return u;
}
static float bits_float(uint32_t u) {
float f;
memcpy(&f, &u, 4);
return f;
}
/* ── integer tests ───────────────────────────────────────────────────────── */
static void test_integer(void) {
printf("\n[1] Integer arithmetic\n");
volatile int32_t a = 0x7FFFFFFF, b = 1;
CHECK((a + b) == (int32_t)0x80000000, "int32 overflow wraps");
volatile uint32_t u = 0xFFFFFFFF;
CHECK(u + 1 == 0, "uint32 wraps to 0");
volatile int64_t la = 0x7FFFFFFFFFFFFFFFLL, lb = 1;
CHECK(la + lb == (int64_t)0x8000000000000000LL, "int64 overflow");
volatile int32_t x = -7, y = 3;
CHECK(x / y == -2, "int divide truncates toward zero: -7/3==-2");
CHECK(x % y == -1, "int modulo sign follows dividend: -7%%3==-1");
volatile uint32_t d = 0xABCDEF12;
uint32_t lo = d & 0xFFFF, hi = d >> 16;
CHECK(lo == 0xEF12 && hi == 0xABCD, "bit shift / mask");
}
/* ── float / double tests ────────────────────────────────────────────────── */
static void test_float(void) {
printf("\n[2] Float / double arithmetic\n");
volatile float fa = 1.0f / 3.0f;
volatile double da = 1.0 / 3.0;
CHECK(fabsf(fa - 0.333333f) < 1e-5f, "float 1/3 within 1e-5");
CHECK(fabs (da - 0.3333333333333333) < 1e-15,"double 1/3 within 1e-15");
volatile float pi_f = 3.14159265f;
volatile double pi_d = 3.14159265358979;
CHECK(fabsf(sinf(pi_f)) < 1e-5f, "sinf(pi) ≈ 0");
CHECK(fabs (sin (pi_d)) < 1e-10, "sin(pi) ≈ 0");
/* Float type-punning via memcpy (must be exact) */
float orig = 1.5f;
uint32_t bits = float_bits(orig);
float restored = bits_float(bits);
CHECK(bits == 0x3FC00000, "float 1.5f bit pattern = 0x3FC00000");
CHECK(restored == orig, "float round-trip via memcpy type-pun");
/* Inf / NaN handling */
volatile float inf = 1.0f / 0.0f;
volatile float nan = 0.0f / 0.0f;
CHECK(isinf(inf), "1.0f/0.0f is Inf");
CHECK(isnan(nan), "0.0f/0.0f is NaN");
}
/* ── memory / alignment tests ────────────────────────────────────────────── */
static void test_memory(void) {
printf("\n[3] Memory / alignment\n");
/* Unaligned read via memcpy — safe on all targets */
uint8_t buf[8] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
uint32_t v;
memcpy(&v, buf + 1, 4); /* unaligned offset 1 */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
CHECK(v == 0x22334455, "unaligned big-endian read");
#else
CHECK(v == 0x55443322, "unaligned little-endian read");
#endif
/* memset / memcmp correctness */
static char a[64], b[64];
memset(a, 0xAB, 64);
memset(b, 0xAB, 64);
CHECK(memcmp(a, b, 64) == 0, "memset+memcmp equal");
b[32] = 0x00;
CHECK(memcmp(a, b, 64) != 0, "memcmp detects difference");
/* Stack alignment: double must be on 8-byte boundary */
double stack_d = 3.14;
CHECK(((uintptr_t)&stack_d % 8) == 0, "stack double is 8-byte aligned");
}
/* ── pointer / size tests ────────────────────────────────────────────────── */
static void test_pointers(void) {
printf("\n[4] Pointer / word size\n");
printf(" sizeof(void*) = %zu\n", sizeof(void*));
printf(" sizeof(long) = %zu\n", sizeof(long));
printf(" sizeof(int) = %zu\n", sizeof(int));
printf(" sizeof(size_t) = %zu\n", sizeof(size_t));
CHECK(sizeof(uint8_t) == 1, "uint8_t = 1 byte");
CHECK(sizeof(uint16_t) == 2, "uint16_t = 2 bytes");
CHECK(sizeof(uint32_t) == 4, "uint32_t = 4 bytes");
CHECK(sizeof(uint64_t) == 8, "uint64_t = 8 bytes");
CHECK(sizeof(int) == 4, "int = 4 bytes (ILP32 or LP64)");
}
/* ── endianness ──────────────────────────────────────────────────────────── */
static void test_endian(void) {
printf("\n[5] Endianness\n");
uint32_t x = 0x12345678;
uint8_t *b = (uint8_t *)&x;
const char *order = (b[0] == 0x12) ? "big-endian" : "little-endian";
printf(" byte order: %s (%02x %02x %02x %02x)\n",
order, b[0], b[1], b[2], b[3]);
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
CHECK(b[0] == 0x12, "compile-time BE matches runtime BE");
#else
CHECK(b[0] == 0x78, "compile-time LE matches runtime LE");
#endif
}
/* ── performance micro-benchmark ─────────────────────────────────────────── */
static void test_perf(void) {
printf("\n[6] Performance micro-benchmark\n");
struct timespec t0, t1;
/* Integer multiply-accumulate */
clock_gettime(CLOCK_MONOTONIC, &t0);
volatile uint64_t acc = 0;
for (int i = 0; i < 10000000; i++) acc += (uint64_t)i * (uint64_t)(i + 1);
clock_gettime(CLOCK_MONOTONIC, &t1);
printf(" 10M int64 mul-acc : %lld ms (acc=%llu)\n",
(long long)(ns_elapsed(t0, t1) / 1000000), (unsigned long long)acc);
/* Float sum */
clock_gettime(CLOCK_MONOTONIC, &t0);
volatile float fsum = 0.0f;
for (int i = 0; i < 10000000; i++) fsum += (float)i * 0.001f;
clock_gettime(CLOCK_MONOTONIC, &t1);
printf(" 10M float add : %lld ms (fsum=%.2f)\n",
(long long)(ns_elapsed(t0, t1) / 1000000), (double)fsum);
/* memcpy throughput */
static uint8_t src[1 << 20], dst[1 << 20];
memset(src, 0x55, sizeof(src));
clock_gettime(CLOCK_MONOTONIC, &t0);
for (int i = 0; i < 100; i++) memcpy(dst, src, sizeof(src));
clock_gettime(CLOCK_MONOTONIC, &t1);
int64_t ms = ns_elapsed(t0, t1) / 1000000;
if (ms < 1) ms = 1;
printf(" 100 x 1 MB memcpy : %lld ms (~%lld MB/s)\n",
(long long)ms, (long long)(100 / ms));
}
/* ── system info ─────────────────────────────────────────────────────────── */
static void print_sysinfo(void) {
printf("\n[0] System info\n");
struct utsname u;
if (uname(&u) == 0) {
printf(" sysname : %s\n", u.sysname);
printf(" nodename : %s\n", u.nodename);
printf(" release : %s\n", u.release);
printf(" machine : %s\n", u.machine);
}
printf(" sizeof(void*): %zu (%d-bit)\n", sizeof(void*), (int)(sizeof(void*)*8));
}
/* ── main ────────────────────────────────────────────────────────────────── */
int main(void) {
printf("=== test_optim: cross-compile correctness & perf ===\n");
print_sysinfo();
test_integer();
test_float();
test_memory();
test_pointers();
test_endian();
test_perf();
printf("\n=== Results: %d/%d passed", tests_run - tests_fail, tests_run);
if (tests_fail == 0)
printf(" — ALL PASS ===\n");
else
printf(" — %d FAILED ===\n", tests_fail);
return tests_fail;
}