Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/cases_generator/test_generator.py
12 views
1
# Sorry for using pytest, these tests are mostly just for me.
2
# Use pytest -vv for best results.
3
4
import tempfile
5
6
import generate_cases
7
from parser import StackEffect
8
9
10
def test_effect_sizes():
11
input_effects = [
12
x := StackEffect("x", "", "", ""),
13
y := StackEffect("y", "", "", "oparg"),
14
z := StackEffect("z", "", "", "oparg*2"),
15
]
16
output_effects = [
17
StackEffect("a", "", "", ""),
18
StackEffect("b", "", "", "oparg*4"),
19
StackEffect("c", "", "", ""),
20
]
21
other_effects = [
22
StackEffect("p", "", "", "oparg<<1"),
23
StackEffect("q", "", "", ""),
24
StackEffect("r", "", "", ""),
25
]
26
assert generate_cases.effect_size(x) == (1, "")
27
assert generate_cases.effect_size(y) == (0, "oparg")
28
assert generate_cases.effect_size(z) == (0, "oparg*2")
29
30
assert generate_cases.list_effect_size(input_effects) == (1, "oparg + oparg*2")
31
assert generate_cases.list_effect_size(output_effects) == (2, "oparg*4")
32
assert generate_cases.list_effect_size(other_effects) == (2, "(oparg<<1)")
33
34
assert generate_cases.string_effect_size(generate_cases.list_effect_size(input_effects)) == "1 + oparg + oparg*2"
35
assert generate_cases.string_effect_size(generate_cases.list_effect_size(output_effects)) == "2 + oparg*4"
36
assert generate_cases.string_effect_size(generate_cases.list_effect_size(other_effects)) == "2 + (oparg<<1)"
37
38
39
def run_cases_test(input: str, expected: str):
40
temp_input = tempfile.NamedTemporaryFile("w+")
41
temp_input.write(generate_cases.BEGIN_MARKER)
42
temp_input.write(input)
43
temp_input.write(generate_cases.END_MARKER)
44
temp_input.flush()
45
temp_output = tempfile.NamedTemporaryFile("w+")
46
temp_metadata = tempfile.NamedTemporaryFile("w+")
47
temp_pymetadata = tempfile.NamedTemporaryFile("w+")
48
temp_executor = tempfile.NamedTemporaryFile("w+")
49
a = generate_cases.Analyzer(
50
[temp_input.name],
51
temp_output.name,
52
temp_metadata.name,
53
temp_pymetadata.name,
54
temp_executor.name,
55
)
56
a.parse()
57
a.analyze()
58
if a.errors:
59
raise RuntimeError(f"Found {a.errors} errors")
60
a.write_instructions()
61
temp_output.seek(0)
62
lines = temp_output.readlines()
63
while lines and lines[0].startswith("// "):
64
lines.pop(0)
65
actual = "".join(lines)
66
# if actual.rstrip() != expected.rstrip():
67
# print("Actual:")
68
# print(actual)
69
# print("Expected:")
70
# print(expected)
71
# print("End")
72
assert actual.rstrip() == expected.rstrip()
73
74
def test_inst_no_args():
75
input = """
76
inst(OP, (--)) {
77
spam();
78
}
79
"""
80
output = """
81
TARGET(OP) {
82
spam();
83
DISPATCH();
84
}
85
"""
86
run_cases_test(input, output)
87
88
def test_inst_one_pop():
89
input = """
90
inst(OP, (value --)) {
91
spam();
92
}
93
"""
94
output = """
95
TARGET(OP) {
96
PyObject *value = stack_pointer[-1];
97
spam();
98
STACK_SHRINK(1);
99
DISPATCH();
100
}
101
"""
102
run_cases_test(input, output)
103
104
def test_inst_one_push():
105
input = """
106
inst(OP, (-- res)) {
107
spam();
108
}
109
"""
110
output = """
111
TARGET(OP) {
112
PyObject *res;
113
spam();
114
STACK_GROW(1);
115
stack_pointer[-1] = res;
116
DISPATCH();
117
}
118
"""
119
run_cases_test(input, output)
120
121
def test_inst_one_push_one_pop():
122
input = """
123
inst(OP, (value -- res)) {
124
spam();
125
}
126
"""
127
output = """
128
TARGET(OP) {
129
PyObject *value = stack_pointer[-1];
130
PyObject *res;
131
spam();
132
stack_pointer[-1] = res;
133
DISPATCH();
134
}
135
"""
136
run_cases_test(input, output)
137
138
def test_binary_op():
139
input = """
140
inst(OP, (left, right -- res)) {
141
spam();
142
}
143
"""
144
output = """
145
TARGET(OP) {
146
PyObject *right = stack_pointer[-1];
147
PyObject *left = stack_pointer[-2];
148
PyObject *res;
149
spam();
150
STACK_SHRINK(1);
151
stack_pointer[-1] = res;
152
DISPATCH();
153
}
154
"""
155
run_cases_test(input, output)
156
157
def test_overlap():
158
input = """
159
inst(OP, (left, right -- left, result)) {
160
spam();
161
}
162
"""
163
output = """
164
TARGET(OP) {
165
PyObject *right = stack_pointer[-1];
166
PyObject *left = stack_pointer[-2];
167
PyObject *result;
168
spam();
169
stack_pointer[-1] = result;
170
DISPATCH();
171
}
172
"""
173
run_cases_test(input, output)
174
175
def test_predictions_and_eval_breaker():
176
input = """
177
inst(OP1, (--)) {
178
}
179
inst(OP3, (arg -- res)) {
180
DEOPT_IF(xxx, OP1);
181
CHECK_EVAL_BREAKER();
182
}
183
"""
184
output = """
185
TARGET(OP1) {
186
PREDICTED(OP1);
187
DISPATCH();
188
}
189
190
TARGET(OP3) {
191
PyObject *arg = stack_pointer[-1];
192
PyObject *res;
193
DEOPT_IF(xxx, OP1);
194
stack_pointer[-1] = res;
195
CHECK_EVAL_BREAKER();
196
DISPATCH();
197
}
198
"""
199
run_cases_test(input, output)
200
201
def test_error_if_plain():
202
input = """
203
inst(OP, (--)) {
204
ERROR_IF(cond, label);
205
}
206
"""
207
output = """
208
TARGET(OP) {
209
if (cond) goto label;
210
DISPATCH();
211
}
212
"""
213
run_cases_test(input, output)
214
215
def test_error_if_plain_with_comment():
216
input = """
217
inst(OP, (--)) {
218
ERROR_IF(cond, label); // Comment is ok
219
}
220
"""
221
output = """
222
TARGET(OP) {
223
if (cond) goto label;
224
DISPATCH();
225
}
226
"""
227
run_cases_test(input, output)
228
229
def test_error_if_pop():
230
input = """
231
inst(OP, (left, right -- res)) {
232
ERROR_IF(cond, label);
233
}
234
"""
235
output = """
236
TARGET(OP) {
237
PyObject *right = stack_pointer[-1];
238
PyObject *left = stack_pointer[-2];
239
PyObject *res;
240
if (cond) goto pop_2_label;
241
STACK_SHRINK(1);
242
stack_pointer[-1] = res;
243
DISPATCH();
244
}
245
"""
246
run_cases_test(input, output)
247
248
def test_cache_effect():
249
input = """
250
inst(OP, (counter/1, extra/2, value --)) {
251
}
252
"""
253
output = """
254
TARGET(OP) {
255
PyObject *value = stack_pointer[-1];
256
uint16_t counter = read_u16(&next_instr[0].cache);
257
uint32_t extra = read_u32(&next_instr[1].cache);
258
STACK_SHRINK(1);
259
next_instr += 3;
260
DISPATCH();
261
}
262
"""
263
run_cases_test(input, output)
264
265
def test_suppress_dispatch():
266
input = """
267
inst(OP, (--)) {
268
goto somewhere;
269
}
270
"""
271
output = """
272
TARGET(OP) {
273
goto somewhere;
274
}
275
"""
276
run_cases_test(input, output)
277
278
def test_macro_instruction():
279
input = """
280
inst(OP1, (counter/1, left, right -- left, right)) {
281
op1(left, right);
282
}
283
op(OP2, (extra/2, arg2, left, right -- res)) {
284
res = op2(arg2, left, right);
285
}
286
macro(OP) = OP1 + cache/2 + OP2;
287
inst(OP3, (unused/5, arg2, left, right -- res)) {
288
res = op3(arg2, left, right);
289
}
290
family(op, INLINE_CACHE_ENTRIES_OP) = { OP, OP3 };
291
"""
292
output = """
293
TARGET(OP1) {
294
PyObject *right = stack_pointer[-1];
295
PyObject *left = stack_pointer[-2];
296
uint16_t counter = read_u16(&next_instr[0].cache);
297
op1(left, right);
298
next_instr += 1;
299
DISPATCH();
300
}
301
302
TARGET(OP) {
303
PyObject *_tmp_1 = stack_pointer[-1];
304
PyObject *_tmp_2 = stack_pointer[-2];
305
PyObject *_tmp_3 = stack_pointer[-3];
306
{
307
PyObject *right = _tmp_1;
308
PyObject *left = _tmp_2;
309
uint16_t counter = read_u16(&next_instr[0].cache);
310
op1(left, right);
311
_tmp_2 = left;
312
_tmp_1 = right;
313
}
314
{
315
PyObject *right = _tmp_1;
316
PyObject *left = _tmp_2;
317
PyObject *arg2 = _tmp_3;
318
PyObject *res;
319
uint32_t extra = read_u32(&next_instr[3].cache);
320
res = op2(arg2, left, right);
321
_tmp_3 = res;
322
}
323
next_instr += 5;
324
static_assert(INLINE_CACHE_ENTRIES_OP == 5, "incorrect cache size");
325
STACK_SHRINK(2);
326
stack_pointer[-1] = _tmp_3;
327
DISPATCH();
328
}
329
330
TARGET(OP3) {
331
PyObject *right = stack_pointer[-1];
332
PyObject *left = stack_pointer[-2];
333
PyObject *arg2 = stack_pointer[-3];
334
PyObject *res;
335
res = op3(arg2, left, right);
336
STACK_SHRINK(2);
337
stack_pointer[-1] = res;
338
next_instr += 5;
339
DISPATCH();
340
}
341
"""
342
run_cases_test(input, output)
343
344
def test_array_input():
345
input = """
346
inst(OP, (below, values[oparg*2], above --)) {
347
spam();
348
}
349
"""
350
output = """
351
TARGET(OP) {
352
PyObject *above = stack_pointer[-1];
353
PyObject **values = (stack_pointer - (1 + oparg*2));
354
PyObject *below = stack_pointer[-(2 + oparg*2)];
355
spam();
356
STACK_SHRINK(oparg*2);
357
STACK_SHRINK(2);
358
DISPATCH();
359
}
360
"""
361
run_cases_test(input, output)
362
363
def test_array_output():
364
input = """
365
inst(OP, (unused, unused -- below, values[oparg*3], above)) {
366
spam(values, oparg);
367
}
368
"""
369
output = """
370
TARGET(OP) {
371
PyObject *below;
372
PyObject **values = stack_pointer - (2) + 1;
373
PyObject *above;
374
spam(values, oparg);
375
STACK_GROW(oparg*3);
376
stack_pointer[-1] = above;
377
stack_pointer[-(2 + oparg*3)] = below;
378
DISPATCH();
379
}
380
"""
381
run_cases_test(input, output)
382
383
def test_array_input_output():
384
input = """
385
inst(OP, (values[oparg] -- values[oparg], above)) {
386
spam(values, oparg);
387
}
388
"""
389
output = """
390
TARGET(OP) {
391
PyObject **values = (stack_pointer - oparg);
392
PyObject *above;
393
spam(values, oparg);
394
STACK_GROW(1);
395
stack_pointer[-1] = above;
396
DISPATCH();
397
}
398
"""
399
run_cases_test(input, output)
400
401
def test_array_error_if():
402
input = """
403
inst(OP, (extra, values[oparg] --)) {
404
ERROR_IF(oparg == 0, somewhere);
405
}
406
"""
407
output = """
408
TARGET(OP) {
409
PyObject **values = (stack_pointer - oparg);
410
PyObject *extra = stack_pointer[-(1 + oparg)];
411
if (oparg == 0) { STACK_SHRINK(oparg); goto pop_1_somewhere; }
412
STACK_SHRINK(oparg);
413
STACK_SHRINK(1);
414
DISPATCH();
415
}
416
"""
417
run_cases_test(input, output)
418
419
def test_cond_effect():
420
input = """
421
inst(OP, (aa, input if ((oparg & 1) == 1), cc -- xx, output if (oparg & 2), zz)) {
422
output = spam(oparg, input);
423
}
424
"""
425
output = """
426
TARGET(OP) {
427
PyObject *cc = stack_pointer[-1];
428
PyObject *input = ((oparg & 1) == 1) ? stack_pointer[-(1 + (((oparg & 1) == 1) ? 1 : 0))] : NULL;
429
PyObject *aa = stack_pointer[-(2 + (((oparg & 1) == 1) ? 1 : 0))];
430
PyObject *xx;
431
PyObject *output = NULL;
432
PyObject *zz;
433
output = spam(oparg, input);
434
STACK_SHRINK((((oparg & 1) == 1) ? 1 : 0));
435
STACK_GROW(((oparg & 2) ? 1 : 0));
436
stack_pointer[-1] = zz;
437
if (oparg & 2) { stack_pointer[-(1 + ((oparg & 2) ? 1 : 0))] = output; }
438
stack_pointer[-(2 + ((oparg & 2) ? 1 : 0))] = xx;
439
DISPATCH();
440
}
441
"""
442
run_cases_test(input, output)
443
444
def test_macro_cond_effect():
445
input = """
446
op(A, (left, middle, right --)) {
447
# Body of A
448
}
449
op(B, (-- deep, extra if (oparg), res)) {
450
# Body of B
451
}
452
macro(M) = A + B;
453
"""
454
output = """
455
TARGET(M) {
456
PyObject *_tmp_1 = stack_pointer[-1];
457
PyObject *_tmp_2 = stack_pointer[-2];
458
PyObject *_tmp_3 = stack_pointer[-3];
459
{
460
PyObject *right = _tmp_1;
461
PyObject *middle = _tmp_2;
462
PyObject *left = _tmp_3;
463
# Body of A
464
}
465
{
466
PyObject *deep;
467
PyObject *extra = NULL;
468
PyObject *res;
469
# Body of B
470
_tmp_3 = deep;
471
if (oparg) { _tmp_2 = extra; }
472
_tmp_1 = res;
473
}
474
STACK_SHRINK(1);
475
STACK_GROW((oparg ? 1 : 0));
476
stack_pointer[-1] = _tmp_1;
477
if (oparg) { stack_pointer[-2] = _tmp_2; }
478
stack_pointer[-3] = _tmp_3;
479
DISPATCH();
480
}
481
"""
482
run_cases_test(input, output)
483
484