scripts: Enabled full C exprs in test/bench define ranges

This enables full C exprs in test/bench define ranges by simply passing
them on to the C compiler.

So this:

  defines.N = 'range(1,20+1)'

Becomes this, in N's define function:

  if (i < 0 + ((((20+1)-1-(1))/(1) + 1))) return ((i-(0))*(1) + (1));

Which is a bit of a mess, but generates the correct range at runtime.

This allows for much more flexible exprs in range defines without
needing a full expr parser in Python.

Note though that we need to evaluate the range length at compile time.
This is notably before the test/bench define system is initialized, so
all three range args (start, stop, step) are limited to really only
simple C literals and exprs.
This commit is contained in:
Christopher Haster
2024-11-17 13:43:44 -06:00
parent ef3accc07c
commit 608d8a2bc1
2 changed files with 94 additions and 52 deletions

View File

@@ -17,6 +17,7 @@ import csv
import errno import errno
import fnmatch import fnmatch
import itertools as it import itertools as it
import functools as ft
import os import os
import pty import pty
import re import re
@@ -47,6 +48,24 @@ def openio(path, mode='r', buffering=-1):
else: else:
return open(path, mode, buffering) return open(path, mode, buffering)
# a define range
class DRange:
def __init__(self, start, stop=None, step=None):
if stop is None:
start, stop = None, start
self.start = start if start is not None else '0'
self.stop = stop
self.step = step if step is not None else '1'
def len(self):
return '(((%s)-1-(%s))/(%s) + 1)' % (
self.stop, self.start, self.step)
def next(self, i):
return '((%s)*(%s) + (%s))' % (
i, self.step, self.start)
class BenchCase: class BenchCase:
# create a BenchCase object from a config # create a BenchCase object from a config
def __init__(self, config, args={}): def __init__(self, config, args={}):
@@ -105,21 +124,10 @@ class BenchCase:
# the runner itself. # the runner itself.
vs = [] vs = []
for v_ in csplit(v): for v_ in csplit(v):
m = re.search(r'\brange\b\s*\(' m = re.match(r'^\s*range\b\s*\((?P<range>.*)\)\s*$', v_)
'(?P<start>[^,\s]*)'
'\s*(?:,\s*(?P<stop>[^,\s]*)'
'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
v_)
if m: if m:
start = (int(m.group('start'), 0) vs.append(DRange(*[
if m.group('start') else 0) s.strip() for s in csplit(m.group('range'))]))
stop = (int(m.group('stop'), 0)
if m.group('stop') else None)
step = (int(m.group('step'), 0)
if m.group('step') else 1)
if m.lastindex <= 1:
start, stop = 0, start
vs.append(range(start, stop, step))
else: else:
vs.append(v_) vs.append(v_)
return vs return vs
@@ -384,17 +392,21 @@ def compile(bench_paths, **args):
j = 0 j = 0
for v in vs: for v in vs:
# generate range # generate range
if isinstance(v, range): if isinstance(v, DRange):
f.writeln(4*' '+'if (i < %d) ' f.writeln(4*' '+'if (i < %s + (%s)) '
'return (i-%d)*%d + %d;' % ( 'return %s;' % (
j+len(v), j, v.step, v.start)) j, v.len(),
j += len(v) v.next('i-(%s)' % j)))
j = '%s + %s' % (j, v.len())
# translate index to define # translate index to define
else: else:
f.writeln(4*' '+'if (i == %d) ' f.writeln(4*' '+'if (i == %s) '
'return %s;' % ( 'return %s;' % (
j, v)) j, v))
j += 1; if isinstance(j, str):
j += ' + 1'
else:
j += 1;
f.writeln(4*' '+'__builtin_unreachable();') f.writeln(4*' '+'__builtin_unreachable();')
f.writeln('}') f.writeln('}')
@@ -532,13 +544,22 @@ def compile(bench_paths, **args):
f.writeln(20*' '+'[%d] = {' f.writeln(20*' '+'[%d] = {'
'"%s", &%s, ' '"%s", &%s, '
'__bench__%s__%s__%d, ' '__bench__%s__%s__%d, '
'NULL, %d},' % ( 'NULL, %s},' % (
sorted(suite.defines).index(k), sorted(suite.defines).index(k),
k, k, case.name, k, i, k, k, case.name, k, i,
sum(len(v) ft.reduce(
if isinstance(v, range) lambda x, y:
else 1 '%s + %s' % (x, y)
for v in vs))) if isinstance(
x, str)
or isinstance(
y, str)
else x + y,
(v.len()
if isinstance(
v, DRange)
else 1
for v in vs))))
f.writeln(16*' '+'},') f.writeln(16*' '+'},')
f.writeln(12*' '+'},') f.writeln(12*' '+'},')
f.writeln(12*' '+'.permutations = %d,' % ( f.writeln(12*' '+'.permutations = %d,' % (

View File

@@ -17,6 +17,7 @@ import csv
import errno import errno
import fnmatch import fnmatch
import itertools as it import itertools as it
import functools as ft
import math as mt import math as mt
import os import os
import pty import pty
@@ -48,6 +49,24 @@ def openio(path, mode='r', buffering=-1):
else: else:
return open(path, mode, buffering) return open(path, mode, buffering)
# a define range
class DRange:
def __init__(self, start, stop=None, step=None):
if stop is None:
start, stop = None, start
self.start = start if start is not None else '0'
self.stop = stop
self.step = step if step is not None else '1'
def len(self):
return '(((%s)-1-(%s))/(%s) + 1)' % (
self.stop, self.start, self.step)
def next(self, i):
return '((%s)*(%s) + (%s))' % (
i, self.step, self.start)
class TestCase: class TestCase:
# create a TestCase object from a config # create a TestCase object from a config
def __init__(self, config, args={}): def __init__(self, config, args={}):
@@ -111,21 +130,10 @@ class TestCase:
# the runner itself. # the runner itself.
vs = [] vs = []
for v_ in csplit(v): for v_ in csplit(v):
m = re.search(r'\brange\b\s*\(' m = re.match(r'^\s*range\b\s*\((?P<range>.*)\)\s*$', v_)
'(?P<start>[^,\s]*)'
'\s*(?:,\s*(?P<stop>[^,\s]*)'
'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
v_)
if m: if m:
start = (int(m.group('start'), 0) vs.append(DRange(*[
if m.group('start') else 0) s.strip() for s in csplit(m.group('range'))]))
stop = (int(m.group('stop'), 0)
if m.group('stop') else None)
step = (int(m.group('step'), 0)
if m.group('step') else 1)
if m.lastindex <= 1:
start, stop = 0, start
vs.append(range(start, stop, step))
else: else:
vs.append(v_) vs.append(v_)
return vs return vs
@@ -396,17 +404,21 @@ def compile(test_paths, **args):
j = 0 j = 0
for v in vs: for v in vs:
# generate range # generate range
if isinstance(v, range): if isinstance(v, DRange):
f.writeln(4*' '+'if (i < %d) ' f.writeln(4*' '+'if (i < %s + (%s)) '
'return (i-%d)*%d + %d;' % ( 'return %s;' % (
j+len(v), j, v.step, v.start)) j, v.len(),
j += len(v) v.next('i-(%s)' % j)))
j = '%s + %s' % (j, v.len())
# translate index to define # translate index to define
else: else:
f.writeln(4*' '+'if (i == %d) ' f.writeln(4*' '+'if (i == %s) '
'return %s;' % ( 'return %s;' % (
j, v)) j, v))
j += 1; if isinstance(j, str):
j += ' + 1'
else:
j += 1;
f.writeln(4*' '+'__builtin_unreachable();') f.writeln(4*' '+'__builtin_unreachable();')
f.writeln('}') f.writeln('}')
@@ -550,13 +562,22 @@ def compile(test_paths, **args):
f.writeln(20*' '+'[%d] = {' f.writeln(20*' '+'[%d] = {'
'"%s", &%s, ' '"%s", &%s, '
'__test__%s__%s__%d, ' '__test__%s__%s__%d, '
'NULL, %d},' % ( 'NULL, %s},' % (
sorted(suite.defines).index(k), sorted(suite.defines).index(k),
k, k, case.name, k, i, k, k, case.name, k, i,
sum(len(v) ft.reduce(
if isinstance(v, range) lambda x, y:
else 1 '%s + %s' % (x, y)
for v in vs))) if isinstance(
x, str)
or isinstance(
y, str)
else x + y,
(v.len()
if isinstance(
v, DRange)
else 1
for v in vs))))
f.writeln(16*' '+'},') f.writeln(16*' '+'},')
f.writeln(12*' '+'},') f.writeln(12*' '+'},')
f.writeln(12*' '+'.permutations = %d,' % ( f.writeln(12*' '+'.permutations = %d,' % (