From 608d8a2bc11638b67eb85fd1500d26e927ba5d87 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 17 Nov 2024 13:43:44 -0600 Subject: [PATCH] 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. --- scripts/bench.py | 73 +++++++++++++++++++++++++++++++----------------- scripts/test.py | 73 +++++++++++++++++++++++++++++++----------------- 2 files changed, 94 insertions(+), 52 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index aa9b1d80..6710e22e 100755 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -17,6 +17,7 @@ import csv import errno import fnmatch import itertools as it +import functools as ft import os import pty import re @@ -47,6 +48,24 @@ def openio(path, mode='r', buffering=-1): else: 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: # create a BenchCase object from a config def __init__(self, config, args={}): @@ -105,21 +124,10 @@ class BenchCase: # the runner itself. vs = [] for v_ in csplit(v): - m = re.search(r'\brange\b\s*\(' - '(?P[^,\s]*)' - '\s*(?:,\s*(?P[^,\s]*)' - '\s*(?:,\s*(?P[^,\s]*)\s*)?)?\)', - v_) + m = re.match(r'^\s*range\b\s*\((?P.*)\)\s*$', v_) if m: - start = (int(m.group('start'), 0) - if m.group('start') else 0) - 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)) + vs.append(DRange(*[ + s.strip() for s in csplit(m.group('range'))])) else: vs.append(v_) return vs @@ -384,17 +392,21 @@ def compile(bench_paths, **args): j = 0 for v in vs: # generate range - if isinstance(v, range): - f.writeln(4*' '+'if (i < %d) ' - 'return (i-%d)*%d + %d;' % ( - j+len(v), j, v.step, v.start)) - j += len(v) + if isinstance(v, DRange): + f.writeln(4*' '+'if (i < %s + (%s)) ' + 'return %s;' % ( + j, v.len(), + v.next('i-(%s)' % j))) + j = '%s + %s' % (j, v.len()) # translate index to define else: - f.writeln(4*' '+'if (i == %d) ' + f.writeln(4*' '+'if (i == %s) ' 'return %s;' % ( j, v)) - j += 1; + if isinstance(j, str): + j += ' + 1' + else: + j += 1; f.writeln(4*' '+'__builtin_unreachable();') f.writeln('}') @@ -532,13 +544,22 @@ def compile(bench_paths, **args): f.writeln(20*' '+'[%d] = {' '"%s", &%s, ' '__bench__%s__%s__%d, ' - 'NULL, %d},' % ( + 'NULL, %s},' % ( sorted(suite.defines).index(k), k, k, case.name, k, i, - sum(len(v) - if isinstance(v, range) - else 1 - for v in vs))) + ft.reduce( + lambda x, y: + '%s + %s' % (x, y) + 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(12*' '+'},') f.writeln(12*' '+'.permutations = %d,' % ( diff --git a/scripts/test.py b/scripts/test.py index 56958c82..579c467a 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -17,6 +17,7 @@ import csv import errno import fnmatch import itertools as it +import functools as ft import math as mt import os import pty @@ -48,6 +49,24 @@ def openio(path, mode='r', buffering=-1): else: 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: # create a TestCase object from a config def __init__(self, config, args={}): @@ -111,21 +130,10 @@ class TestCase: # the runner itself. vs = [] for v_ in csplit(v): - m = re.search(r'\brange\b\s*\(' - '(?P[^,\s]*)' - '\s*(?:,\s*(?P[^,\s]*)' - '\s*(?:,\s*(?P[^,\s]*)\s*)?)?\)', - v_) + m = re.match(r'^\s*range\b\s*\((?P.*)\)\s*$', v_) if m: - start = (int(m.group('start'), 0) - if m.group('start') else 0) - 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)) + vs.append(DRange(*[ + s.strip() for s in csplit(m.group('range'))])) else: vs.append(v_) return vs @@ -396,17 +404,21 @@ def compile(test_paths, **args): j = 0 for v in vs: # generate range - if isinstance(v, range): - f.writeln(4*' '+'if (i < %d) ' - 'return (i-%d)*%d + %d;' % ( - j+len(v), j, v.step, v.start)) - j += len(v) + if isinstance(v, DRange): + f.writeln(4*' '+'if (i < %s + (%s)) ' + 'return %s;' % ( + j, v.len(), + v.next('i-(%s)' % j))) + j = '%s + %s' % (j, v.len()) # translate index to define else: - f.writeln(4*' '+'if (i == %d) ' + f.writeln(4*' '+'if (i == %s) ' 'return %s;' % ( j, v)) - j += 1; + if isinstance(j, str): + j += ' + 1' + else: + j += 1; f.writeln(4*' '+'__builtin_unreachable();') f.writeln('}') @@ -550,13 +562,22 @@ def compile(test_paths, **args): f.writeln(20*' '+'[%d] = {' '"%s", &%s, ' '__test__%s__%s__%d, ' - 'NULL, %d},' % ( + 'NULL, %s},' % ( sorted(suite.defines).index(k), k, k, case.name, k, i, - sum(len(v) - if isinstance(v, range) - else 1 - for v in vs))) + ft.reduce( + lambda x, y: + '%s + %s' % (x, y) + 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(12*' '+'},') f.writeln(12*' '+'.permutations = %d,' % (