Nix flake for RPython interpreters
Revisão | 86c548543683f7b611db4351c39fce3e3147c885 (tree) |
---|---|
Hora | 2024-04-20 04:17:28 |
Autor | Corbin <cds@corb...> |
Commiter | Corbin |
bf: Add optimizing ops, peephole, delooper.
Simpler than it sounds! I get to exploit the Abelian group actions on
the two pairs of ops Inc/Dec and Right/Left to track them with integers,
and they are free other than the type tag; this works because integers
are the initial Abelian group.
I've seen a couple folks go further down this particular direction, but
I'm going to say that this is sufficient for now, because we're now 20%
faster on Mandelbrot than Brown's original interpreter. Our main source
of speedups is fundamentally from reducing the tape from a
dynamically-allocated compound structure into a statically-allocated
arena. We also use op fusion to overcome otherwise-difficult sequences
for the JIT, like repeated subtraction and copying/moving memory.
@@ -13,7 +13,8 @@ def printableProgram(program): return program.asStr() | ||
13 | 13 | jitdriver = JitDriver(greens=['program'], reds=['position', 'tape'], |
14 | 14 | get_printable_location=printableProgram) |
15 | 15 | |
16 | -class Op(object): pass | |
16 | +class Op(object): | |
17 | + _immutable_fields_ = "width", | |
17 | 18 | |
18 | 19 | class _Input(Op): |
19 | 20 | def asStr(self): return ',' |
@@ -27,35 +28,41 @@ class _Output(Op): | ||
27 | 28 | os.write(1, chr(tape[position])) |
28 | 29 | return position |
29 | 30 | Output = _Output() |
30 | -class _Inc(Op): | |
31 | - def asStr(self): return '+' | |
31 | +class Add(Op): | |
32 | + _immutable_fields_ = "imm", | |
33 | + def __init__(self, imm): self.imm = imm | |
34 | + def asStr(self): return "add(%d)" % self.imm | |
32 | 35 | def runOn(self, tape, position): |
33 | - tape[position] += 1 | |
36 | + tape[position] += self.imm | |
34 | 37 | return position |
35 | -Inc = _Inc() | |
36 | -class _Dec(Op): | |
37 | - def asStr(self): return '-' | |
38 | +Inc = Add(1) | |
39 | +Dec = Add(-1) | |
40 | +class Shift(Op): | |
41 | + _immutable_fields_ = "width", | |
42 | + def __init__(self, width): self.width = width | |
43 | + def asStr(self): return "shift(%d)" % self.width | |
44 | + def runOn(self, tape, position): return position + self.width | |
45 | +Left = Shift(-1) | |
46 | +Right = Shift(1) | |
47 | +class _Zero(Op): | |
48 | + def asStr(self): return "0" | |
38 | 49 | def runOn(self, tape, position): |
39 | - tape[position] -= 1 | |
50 | + tape[position] = 0 | |
40 | 51 | return position |
41 | -Dec = _Dec() | |
42 | -class _Left(Op): | |
43 | - def asStr(self): return '<' | |
52 | +Zero = _Zero() | |
53 | +class ZeroAdd(Op): | |
54 | + _immutable_fields_ = "offset", | |
55 | + def __init__(self, offset): self.offset = offset | |
56 | + def asStr(self): return "zeroadd(%d)" % self.offset | |
44 | 57 | def runOn(self, tape, position): |
45 | - if position == 0: raise Exception("Stack underflow") | |
46 | - return position - 1 | |
47 | -Left = _Left() | |
48 | -class _Right(Op): | |
49 | - def asStr(self): return '>' | |
50 | - def runOn(self, tape, position): | |
51 | - if len(tape) <= position: tape.extend([0] * 1000) | |
52 | - return position + 1 | |
53 | -Right = _Right() | |
58 | + tape[position + self.offset] += tape[position] | |
59 | + tape[position] = 0 | |
60 | + return position | |
54 | 61 | class Loop(Op): |
55 | 62 | _immutable_fields_ = "ops[*]", |
56 | 63 | def __init__(self, ops): self.ops = ops |
57 | 64 | def asStr(self): |
58 | - return '[' + ''.join([op.asStr() for op in self.ops]) + ']' | |
65 | + return '[' + '; '.join([op.asStr() for op in self.ops]) + ']' | |
59 | 66 | def runOn(self, tape, position): |
60 | 67 | while tape[position]: |
61 | 68 | jitdriver.jit_merge_point(program=self, |
@@ -63,6 +70,35 @@ class Loop(Op): | ||
63 | 70 | for op in self.ops: position = op.runOn(tape, position) |
64 | 71 | return position |
65 | 72 | |
73 | +def peep(ops): | |
74 | + rv = [] | |
75 | + temp = ops[0] | |
76 | + for op in ops[1:]: | |
77 | + if isinstance(temp, Shift) and isinstance(op, Shift): | |
78 | + temp = Shift(temp.width + op.width) | |
79 | + elif isinstance(temp, Add) and isinstance(op, Add): | |
80 | + temp = Add(temp.imm + op.imm) | |
81 | + else: | |
82 | + rv.append(temp) | |
83 | + temp = op | |
84 | + rv.append(temp) | |
85 | + return rv | |
86 | + | |
87 | +def oppositeShifts(op1, op2): | |
88 | + if not isinstance(op1, Shift) or not isinstance(op2, Shift): return False | |
89 | + return op1.width == -op2.width | |
90 | + | |
91 | +def isConstAdd(op, imm): return isinstance(op, Add) and op.imm == imm | |
92 | + | |
93 | +def loopish(ops): | |
94 | + if len(ops) == 1 and isConstAdd(ops[0], -1): | |
95 | + return Zero | |
96 | + elif (len(ops) == 4 and | |
97 | + isConstAdd(ops[0], -1) and isConstAdd(ops[2], 1) and | |
98 | + oppositeShifts(ops[1], ops[3])): | |
99 | + return ZeroAdd(ops[1].width) | |
100 | + return Loop(ops[:]) | |
101 | + | |
66 | 102 | parseTable = { |
67 | 103 | ',': Input, '.': Output, |
68 | 104 | '+': Inc, '-': Dec, |
@@ -75,17 +111,22 @@ def parse(s): | ||
75 | 111 | if char in parseTable: ops[-1].append(parseTable[char]) |
76 | 112 | elif char == '[': ops.append([]) |
77 | 113 | elif char == ']': |
78 | - loop = Loop(ops.pop()[:]) | |
114 | + loop = loopish(peep(ops.pop())) | |
79 | 115 | ops[-1].append(loop) |
80 | 116 | |
81 | - return ops.pop() | |
117 | + return peep(ops.pop()) | |
82 | 118 | |
83 | 119 | def entryPoint(argv): |
84 | - if len(argv) < 2: | |
85 | - print "You must supply a filename" | |
120 | + if len(argv) < 2 or "-h" in argv: | |
121 | + print "Usage: bf [-c <number of cells>] [-h] <program.bf>" | |
86 | 122 | return 1 |
87 | - with open(argv[1]) as handle: program = parse(handle.read()) | |
88 | - tape = [0] * 30000 | |
123 | + cells = 30000 | |
124 | + if argv[1] == "-c": | |
125 | + cells = int(argv[2]) | |
126 | + path = argv[3] | |
127 | + else: path = argv[1] | |
128 | + with open(path) as handle: program = parse(handle.read()) | |
129 | + tape = [0] * cells | |
89 | 130 | position = 0 |
90 | 131 | for op in program: position = op.runOn(tape, position) |
91 | 132 | return 0 |