A categorical programming language
Révision | be55e727840cd7ed53fc8a9d8a92802aedf578f8 (tree) |
---|---|
l'heure | 2022-04-13 13:52:38 |
Auteur | Corbin <cds@corb...> |
Commiter | Corbin |
Start optimizing emitted bytecode.
@@ -277,7 +277,7 @@ FZero = makeConstantF(0.0) | ||
277 | 277 | FOne = makeConstantF(1.0) |
278 | 278 | FPi = makeConstantF(math.pi) |
279 | 279 | |
280 | -def sign(f): return math.copysign(1.0, f) > 0.0 | |
280 | +def sign(f): return abs(f) == f | |
281 | 281 | class FSign(Arrow): |
282 | 282 | _immutable_ = True |
283 | 283 | def compile(self, compiler): compiler.sign() |
@@ -69,11 +69,11 @@ class CAM(object): | ||
69 | 69 | return runCAM(self.code, self.constants, term) |
70 | 70 | |
71 | 71 | |
72 | -def location(pc, code): | |
72 | +def location(pc, code, constants): | |
73 | 73 | return "%d: %s" % (pc, decode(code[pc])) |
74 | 74 | |
75 | 75 | CAMDriver = JitDriver(name="cam", |
76 | - greens=["pc", "code"], reds="auto", | |
76 | + greens=["pc", "code", "constants"], reds=["term", "stack"], | |
77 | 77 | get_printable_location=location, |
78 | 78 | is_recursive=True) |
79 | 79 |
@@ -82,7 +82,8 @@ def runCAM(code, constants, term): | ||
82 | 82 | stack = T() |
83 | 83 | |
84 | 84 | while pc < len(code): |
85 | - CAMDriver.jit_merge_point(code=code, pc=pc) | |
85 | + CAMDriver.jit_merge_point(pc=pc, code=code, constants=constants, | |
86 | + term=term, stack=stack) | |
86 | 87 | op = code[pc]; pc += 1 |
87 | 88 | |
88 | 89 | # print "pc", self.pc, "op", decode(op) |
@@ -116,13 +117,19 @@ def runCAM(code, constants, term): | ||
116 | 117 | stack = P(N(pc), stack.second()) |
117 | 118 | term = P(v1, v2) |
118 | 119 | pc = l |
120 | + CAMDriver.can_enter_jit(pc=pc, code=code, constants=constants, | |
121 | + term=term, stack=stack) | |
119 | 122 | elif op == BRANCH: |
120 | 123 | term = L(T()) if term.b() else R(T()) |
121 | 124 | elif op == RET: |
122 | 125 | pc = stack.first().n() |
123 | 126 | stack = stack.second() |
127 | + CAMDriver.can_enter_jit(pc=pc, code=code, constants=constants, | |
128 | + term=term, stack=stack) | |
124 | 129 | elif op == GOTO: |
125 | 130 | pc = code[pc] |
131 | + CAMDriver.can_enter_jit(pc=pc, code=code, constants=constants, | |
132 | + term=term, stack=stack) | |
126 | 133 | elif op == HALT: |
127 | 134 | return term |
128 | 135 | elif op == LEFT: |
@@ -194,6 +201,14 @@ class Compiler(object): | ||
194 | 201 | Compile an arrow to CAM bytecode. |
195 | 202 | """ |
196 | 203 | |
204 | + # The location behind which we will not erase any instructions during | |
205 | + # peephole optimization. Labels behind the barrier will not shift. | |
206 | + barrier = 0 | |
207 | + | |
208 | + # The last location that was written, to make it easy to erase bytecodes | |
209 | + # with parameters. | |
210 | + lastLocation = 0 | |
211 | + | |
197 | 212 | def __init__(self): |
198 | 213 | self.constants = [] |
199 | 214 | self.insts = [] |
@@ -204,6 +219,19 @@ class Compiler(object): | ||
204 | 219 | # Queue of curried arrows to compile. |
205 | 220 | self.curryQueue = [] |
206 | 221 | |
222 | + def emit(self, inst): | |
223 | + self.insts.append(inst) | |
224 | + | |
225 | + def emitParam(self, inst, param): | |
226 | + self.lastLocation = len(self.insts) | |
227 | + self.insts.append(inst) | |
228 | + self.insts.append(param) | |
229 | + | |
230 | + def peek(self, inst): | |
231 | + # if self.lastLocation < len(self.insts): | |
232 | + # return self.insts[self.lastLocation] == inst | |
233 | + return False | |
234 | + | |
207 | 235 | def quote(self, constant): |
208 | 236 | # Don't repeat constants. |
209 | 237 | for i, const in enumerate(self.constants): |
@@ -213,18 +241,26 @@ class Compiler(object): | ||
213 | 241 | else: |
214 | 242 | c = len(self.constants) |
215 | 243 | self.constants.append(constant) |
216 | - self.insts.append(QUOTE) | |
217 | - self.insts.append(c) | |
244 | + if self.peek(QUOTE): | |
245 | + # Peephole: QUOTE c; QUOTE d -> QUOTE d | |
246 | + # XXX icky patch | |
247 | + # XXX respect the barrier!! | |
248 | + self.insts[-1] = c | |
249 | + else: | |
250 | + self.emitParam(QUOTE, c) | |
218 | 251 | |
219 | 252 | # A label is a location where execution can go. A (forward) jump is a |
220 | 253 | # location that needs to be patched with a label. Backward jumps are |
221 | - # emitted immediately without a patch site. | |
254 | + # emitted immediately without a patch site. To protect labels from | |
255 | + # shifting during peephole optimization, a barrier is shifted forward to | |
256 | + # commit to each label as it is taken. | |
222 | 257 | |
223 | 258 | def here(self): |
224 | - return len(self.insts) | |
259 | + rv = self.barrier = len(self.insts) | |
260 | + return rv | |
225 | 261 | |
226 | 262 | def jumpForward(self): |
227 | - rv = len(self.insts) | |
263 | + rv = self.barrier = len(self.insts) | |
228 | 264 | self.insts.append(-1) |
229 | 265 | return rv |
230 | 266 |
@@ -232,7 +268,7 @@ class Compiler(object): | ||
232 | 268 | self.insts.append(label) |
233 | 269 | |
234 | 270 | def jumpHere(self, jump): |
235 | - self.insts[jump] = len(self.insts) | |
271 | + self.insts[jump] = self.barrier = len(self.insts) | |
236 | 272 | |
237 | 273 | def curry(self, arrow): |
238 | 274 | self.insts.append(CUR) |
@@ -244,6 +280,7 @@ class Compiler(object): | ||
244 | 280 | self.halt() |
245 | 281 | while self.curryQueue: |
246 | 282 | jump, arrow = self.curryQueue.pop() |
283 | + # XXX if already seen this arrow, then jump to it instead | |
247 | 284 | self.jumpHere(jump) |
248 | 285 | arrow.compile(self) |
249 | 286 | self.ret() |
@@ -251,38 +288,38 @@ class Compiler(object): | ||
251 | 288 | def makeMachine(self): |
252 | 289 | return CAM(self.insts[:], self.constants[:]) |
253 | 290 | |
254 | - def fst(self): self.insts.append(FST) | |
255 | - def snd(self): self.insts.append(SND) | |
256 | - def push(self): self.insts.append(PUSH) | |
257 | - def pop(self): self.insts.append(POP) | |
258 | - def swap(self): self.insts.append(SWAP) | |
259 | - def cons(self): self.insts.append(CONS) | |
260 | - def app(self): self.insts.append(APP) | |
261 | - def branch(self): self.insts.append(BRANCH) | |
262 | - def ret(self): self.insts.append(RET) | |
263 | - def goto(self): self.insts.append(GOTO) | |
264 | - def halt(self): self.insts.append(HALT) | |
265 | - def left(self): self.insts.append(LEFT) | |
266 | - def right(self): self.insts.append(RIGHT) | |
267 | - def gotoRight(self): self.insts.append(GOTORIGHT) | |
268 | - def notOp(self): self.insts.append(NOTOP) | |
269 | - def andOp(self): self.insts.append(ANDOP) | |
270 | - def orOp(self): self.insts.append(OROP) | |
271 | - def pred(self): self.insts.append(PRED) | |
272 | - def succ(self): self.insts.append(SUCC) | |
273 | - def isNil(self): self.insts.append(ISNIL) | |
274 | - def assoc(self): self.insts.append(ASSOC) | |
275 | - def sign(self): self.insts.append(SIGN) | |
276 | - def floor(self): self.insts.append(FLOOR) | |
277 | - def negate(self): self.insts.append(NEGATE) | |
278 | - def recip(self): self.insts.append(RECIP) | |
279 | - def lessThan(self): self.insts.append(LESSTHAN) | |
280 | - def add(self): self.insts.append(ADD) | |
281 | - def mul(self): self.insts.append(MUL) | |
282 | - def sqrt(self): self.insts.append(SQRT) | |
283 | - def sin(self): self.insts.append(SIN) | |
284 | - def cos(self): self.insts.append(COS) | |
285 | - def atan2(self): self.insts.append(ATAN2) | |
291 | + def fst(self): self.emit(FST) | |
292 | + def snd(self): self.emit(SND) | |
293 | + def push(self): self.emit(PUSH) | |
294 | + def pop(self): self.emit(POP) | |
295 | + def swap(self): self.emit(SWAP) | |
296 | + def cons(self): self.emit(CONS) | |
297 | + def app(self): self.emit(APP) | |
298 | + def branch(self): self.emit(BRANCH) | |
299 | + def ret(self): self.emit(RET) | |
300 | + def goto(self): self.emit(GOTO) | |
301 | + def halt(self): self.emit(HALT) | |
302 | + def left(self): self.emit(LEFT) | |
303 | + def right(self): self.emit(RIGHT) | |
304 | + def gotoRight(self): self.emit(GOTORIGHT) | |
305 | + def notOp(self): self.emit(NOTOP) | |
306 | + def andOp(self): self.emit(ANDOP) | |
307 | + def orOp(self): self.emit(OROP) | |
308 | + def pred(self): self.emit(PRED) | |
309 | + def succ(self): self.emit(SUCC) | |
310 | + def isNil(self): self.emit(ISNIL) | |
311 | + def assoc(self): self.emit(ASSOC) | |
312 | + def sign(self): self.emit(SIGN) | |
313 | + def floor(self): self.emit(FLOOR) | |
314 | + def negate(self): self.emit(NEGATE) | |
315 | + def recip(self): self.emit(RECIP) | |
316 | + def lessThan(self): self.emit(LESSTHAN) | |
317 | + def add(self): self.emit(ADD) | |
318 | + def mul(self): self.emit(MUL) | |
319 | + def sqrt(self): self.emit(SQRT) | |
320 | + def sin(self): self.emit(SIN) | |
321 | + def cos(self): self.emit(COS) | |
322 | + def atan2(self): self.emit(ATAN2) | |
286 | 323 | |
287 | 324 | # Generate most of the emission methods. |
288 | 325 | # insts = ("fst", "snd", "push", "pop", "swap", "cons", "app", "branch", "ret", |
@@ -1,6 +1,6 @@ | ||
1 | 1 | import math |
2 | 2 | |
3 | -from rpython.rlib.jit import promote, we_are_jitted | |
3 | +from rpython.rlib.jit import we_are_jitted | |
4 | 4 | # from rpython.rlib.rbigint import rbigint |
5 | 5 | |
6 | 6 |
@@ -109,30 +109,10 @@ class R(Element): | ||
109 | 109 | def asStr(self): |
110 | 110 | return "R(%s)" % self._x.asStr() |
111 | 111 | |
112 | -class H(Element): | |
113 | - _immutable_ = True | |
114 | - def __init__(self, f, x): | |
115 | - self._f = f | |
116 | - self._x = x | |
117 | - def apply(self, y): | |
118 | - # Defunctionalize by assuming that f can only take on finitely many | |
119 | - # values. This trick was suggested by cfbolz; they do a similar trick | |
120 | - # in Pycket for similar ends. | |
121 | - return promote(self._f).run(P(self._x, y)) | |
122 | - def asStr(self): | |
123 | - return "%s @ %s" % (self._f, self._x.asStr()) | |
124 | - | |
125 | -class Xs(Element): | |
126 | - _immutable_ = True | |
127 | - _immutable_fields_ = "xs[*]", | |
128 | - def __init__(self, xs): | |
129 | - self.xs = xs | |
130 | - def l(self): return self.xs | |
131 | - def asStr(self): | |
132 | - return "[%s]" % ", ".join([x.asStr() for x in self.xs]) | |
133 | - | |
134 | 112 | |
135 | 113 | def equal(e1, e2): |
114 | + if e1 is e2: | |
115 | + return True | |
136 | 116 | return False |
137 | 117 | # if isinstance(e1, T) and isinstance(e2, T): |
138 | 118 | # return True |
@@ -150,12 +130,3 @@ def equal(e1, e2): | ||
150 | 130 | # return equal(e1._x, e2._x) |
151 | 131 | # elif isinstance(e1, R) and isinstance(e2, R): |
152 | 132 | # return equal(e1._x, e2._x) |
153 | - # elif isinstance(e1, Xs) and isinstance(e2, Xs): | |
154 | - # if len(e1.xs) != len(e2.xs): | |
155 | - # return False | |
156 | - # for i, x in enumerate(e1.xs): | |
157 | - # if not equal(x, e2.xs[i]): | |
158 | - # return False | |
159 | - # return True | |
160 | - # else: | |
161 | - # return False |
@@ -97,6 +97,17 @@ | ||
97 | 97 | recompute jumps |
98 | 98 | * TCO, or really any call optimizations |
99 | 99 | * snoc and other backwards versions of ops |
100 | + * term ops can be applied to quoted elements, creating new quotes with | |
101 | + pre-computed elements | |
102 | + * needs equality for elements | |
103 | + * curried arrows sometimes repeat, or at least they compile down to the same | |
104 | + code | |
105 | + * needs equality for arrows | |
106 | + * sometimes we end up with two quotes in a row, should only use the second | |
107 | + * requires code barrier | |
108 | + * Currying everything really does look like it might generate better | |
109 | + bytecode | |
110 | + * Or at least let binary term ops be stack ops? | |
100 | 111 | * fun/precomp is an ingredient of CPS transformation |
101 | 112 | * https://okmij.org/ftp/continuations/undelimited.html |
102 | 113 | * A serious cost model |